PowerShell中执行错误的catch块执行

时间:2018-05-19 10:36:59

标签: powershell exception-handling

我是C#开发人员,有时PowerShell只会让我发疯。

我有以下代码:

$script:ErrorActionPreference = 'Stop'

try {
    # Some code here
}
catch [Microsoft.PowerShell.Commands.WriteErrorException] {
    # Print error messages (without stacktrace)
    Write-Host -ForegroundColor Red $_.Exception.Message
    exit 1
}
catch [System.Management.Automation.RuntimeException] {
    # A thrown string
    Write-Host -ForegroundColor Red $_.Exception.Message
    Write-Host -ForegroundColor Red $_.ScriptStackTrace
    exit 1
}
catch {
    # Print proper exception message (including stack trace)
    Write-Host -ForegroundColor Red "$($_.Exception.GetType().Name): $($_.Exception.Message)"
    Write-Host -ForegroundColor Red $_.ScriptStackTrace
    exit 1
}

这个想法基本上是:

  1. 如果异常来自对Write-Error的调用,请使用第一个 catch块。
  2. 如果直接抛出字符串,请使用第二个 catch块。
  3. 对于任何其他例外,请使用 last catch块。
  4. 现在,我的问题在于Write-Error和第一个catch块:

    • 如果我在Write-Error块内调用try,则执行第二个catch块(即使应该执行第一个块)。
    • 如果我删除第二个 catch块然后调用Write-Error,则使用正确的(第一个)catch块。

    为什么?

    我已经检查过WriteErrorExceptionRuntimeException是否相互继承:它们不是(都来自SystemException,但这无关紧要。)

    我还验证了PowerShell 5.1和PowerShell Core(6.0)中的这种行为是相同的。

1 个答案:

答案 0 :(得分:1)

默认情况下,

Write-Error不会抛出终止错误,但正如您所提到的那样,ErrorActionPreference设置为Stop。但是,这会将抛出的异常更改为ActionPreferenceStopException 继承RuntimeException

您仍然可以在没有WriteErrorException子句的情况下捕获RuntimeException,因为ActionPreferenceStopException的内部错误记录包含WriteErrorException

你可以通过运行这个来看看我的意思:

Write-Error 'this is a test' -ErrorAction Stop
$error[0].ErrorRecord.Exception.GetType()
# IsPublic IsSerial Name                        BaseType
# -------- -------- ----                        --------
# True     True     WriteErrorException         System.SystemException

但是使用RuntimeException子句,它将首先被选中,因为RuntimeException是最接近的匹配异常类型。

要解决此问题,您需要在$_子句中抛出更具体的异常或测试RuntimeException。这是后者

$script:ErrorActionPreference = 'Stop'

try {
    # Some code here
}
catch [Microsoft.PowerShell.Commands.WriteErrorException] {
    # Print error messages (without stacktrace)
    Write-Host -ForegroundColor Red $_.Exception.Message
    exit 1
}
catch [System.Management.Automation.RuntimeException] {
    if ($_.Exception -is [Microsoft.PowerShell.Commands.WriteErrorException]) {
        # Print error messages (without stacktrace)
        Write-Host -ForegroundColor Red $_.Exception.Message
        exit 1
    }

    # A thrown string
    Write-Host -ForegroundColor Red $_.Exception.Message
    Write-Host -ForegroundColor Red $_.ScriptStackTrace
    exit 1
}
catch {
    # Print proper exception message (including stack trace)
    Write-Host -ForegroundColor Red "$($_.Exception.GetType().Name): $($_.Exception.Message)"
    Write-Host -ForegroundColor Red $_.ScriptStackTrace
    exit 1
}

您还可以添加ActionPreferenceStopException子句并在那里测试$_

编辑:实际上,除非确实想要使用Write-Error,否则最好只抛出类似于C#的异常。因此,请使用:

而不是Write-Error
throw [System.InvalidOperationException]::new('This is my message')