【问题标题】:Catching Cascading Errors in PowerShell在 PowerShell 中捕获级联错误
【发布时间】:2017-11-10 11:51:11
【问题描述】:

我正在使用一个包装脚本,该脚本调用一个函数,该函数查询指定服务器内的某些数据库并将元数据插入到单独服务器上的特定数据库中。我使用 $error.count 变量来确定脚本是否成功。我预计会发生一些权限/提取错误,并希望这些错误被捕获和忽略($error.count 变量没有增加,但在作业日志中写入了警告)。我可以确认一个权限错误正在发生并且被正确捕获。 $error.count 变量没有增加,但从 catch 中打印出一条警告,显示无法访问的数据库。

我的问题是在提取/插入功能运行完成后出现的。在此函数返回包装脚本后,我立即再次打印了 $error.count 变量。这一次,它返回一个 1,就好像先前捕获的错误级联到包装脚本中一样。正如我之前提到的,我不希望这包括在错误计数中。我不确定 $error.count 是如何或为什么从这个函数中增加的。

我是否应该使用不同的变量来确定脚本是否“失败”?是否有一些潜在的原因导致 $error.count 会在有错误的函数之外增加而在捕获错误后不增加?对此问题的任何指导将不胜感激。

参考代码: 包装函数:

$errorCount = $error.count
    Write-Warning ("$errorCount Before function")
    Extraction/Insertion_Function -serverList $serverList -insertionDB $insertionDB -ErrorAction SilentlyContinue
    $errorCount = $error.count
    Write-Warning ("$errorCount After function")
    } catch {
    Write-Error "Error caught by wrapper: $_"
    }

提取/插入_函数:

ForEach ($db in $dbList) {
Write-Warning "$errorCount database
.
.
.
    try {
            $totalProperties = Get-ServerDBMetadata -DBConnectionString ($connStr) -DatabaseName $dbName -EA SilentlyContinue 
            } catch {
                Write-Warning "Unable to extract metadata from $dbname in $server"
                }
}

然后我在从每个数据库中提取/插入元数据到插入数据库的循环中打印出错误计数,并在每个包含数据库的服务器的循环中打印出错误计数:

WARNING: 0 Before function
WARNING: 0 database
.
.
.
WARNING: 0 database
WARNING: Unable to extract metadata from *database* in *server*
WARNING: 0 database
.
.
.
WARNING: 0 database
**WARNING: 1 After function**

错误(权限问题)在函数内部被捕获,但级联到我的包装脚本。我希望忽略此特定错误,同时不忽略其他更严重的错误(例如无法连接到我将元数据插入其中的服务器),因此将 -EA Ignore 放置在包装脚本内的驱动程序函数上是不可行的问题。

【问题讨论】:

  • 这里没有足够的代码来确定您在做什么。请提供minimal reproducible example。不过,您应该知道一件事,$error.Count 不是变量。 $error 是一个(特殊)变量,它是一个包含所有错误的数组,所以$error.Count 显示了数组的大小。 $error 追加到开头,而不是结尾,所以 $error[0] 始终是最新的错误。如果您想知道计数增加的原因,请在调用前后查看$error[0],因为实际错误将为您提供有关发生了什么的线索。
  • 我知道发生了什么。它是一个权限问题。我的问题是我想忽略那个特定的问题。正如您通过打印输出的错误计数所看到的那样,它被捕获并且不包含在发生错误的函数内部的错误计数中,但包含在包装脚本中的函数之外。我想知道为什么会这样,这样我才能弄清楚如何正确地忽略它。或者,如果可以使用更好的错误处理“变量”,我想知道那是什么。

标签: powershell error-handling


【解决方案1】:

try-catch 未捕获错误的主要问题(即使您没有提供所有代码)是您的 cmdlet 显式调用 -ErrorAction SilentlyContinue。 Try/Catch 块需要使用终止错误,因此对于您的函数/cmdlet,您需要更改为 -ErrorAction Stop 以便 try/catch 正确处理来自该函数/cmdlet 的错误。

这需要针对我们看不到的代码中的任何其他函数/cmdlet 进行更新。

编辑在下面的 cmets 中描述:

 $n = New-Object PSObject -property @{
    'Test1' = ''
    'Test2' = ''
    'Test3' = ''
}

try {
    get-process someprocess -ErrorAction Stop
    $n.Test1 = $true
} catch {
    $n.Test1 = $false
}

try {
    Get-WmiObject win32_computersystem -ErrorAction Stop
    $n.Test2 = $true
} catch {
    $n.Test2 = $false
}

try {
    Get-Content somefile.ext -ErrorAction Stop
    $n.Test3 = $true
} catch {
    $n.Test3 = $false
}


if ($n.Test1 -and $n.Test2 -and $n.Test3) {
    ## All procedures completed successfully -- do something magical
} else {
    ## At least one test procedure failed.
}

【讨论】:

  • 函数内捕获的错误在函数完成后是否仍然作为错误返回?似乎我的 Extraction/Insertion_Function 内部的错误被捕获,但是当该函数完成并且我的包装器继续执行时,错误会冒泡备份堆栈作为非终止错误。
  • 听起来您正在尝试自己创建一个笨拙的错误处理机制来确定代码不同部分的成功或失败。如果我有一个依赖于一堆其他进程成功的进程,我会按照上面编辑中指示的方式处理它。尝试/捕获您的特定段,而不是计算 $error 变量,而是对其进行显式测试(见上文)。
【解决方案2】:

记录自上次日志以来的剩余错误:

While ($Global:ErrorCount -lt $Error.Count) {
    $Err = $Error[$Error.Count - ++$Global:ErrorCount]
    $ErrLine = "Error at $($Err.InvocationInfo.ScriptLineNumber),$($Err.InvocationInfo.OffsetInLine): $Err"
    Write-Host $ErrLine -ForegroundColor Red        # Log this
}

【讨论】:

    【解决方案3】:

    SilentlyContinue 替换为Ignore 以忽略错误并且不让它增加计数。

    Extraction/Insertion_Function -serverList $serverList -insertionDB $insertionDB -ErrorAction Ignore
    

    要在函数内捕获它,请使用-ErrorAction Stop,就像在 pip3r 的答案中一样,因为 try/catch 语句只捕获终止错误。

    【讨论】:

    • 即使在包装脚本中忽略驱动程序函数,错误计数仍然会在函数之外增加。此外,我不希望整个驱动程序函数都使用 -EA Ignore,因为我不想忽略潜在的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-20
    • 1970-01-01
    • 2017-03-08
    • 2018-04-08
    • 1970-01-01
    • 2021-02-11
    • 1970-01-01
    相关资源
    最近更新 更多