我是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
}

这个想法基本上是:
  • 如果异常来自对Write-Error的调用,请使用第一个catch块。
  • 如果直接抛出字符串,请使用第二个catch块。
  • 对于其他任何异常,请使用最后一个catch块。

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

  • 这是为什么?

    我检查了WriteErrorExceptionRuntimeException是否彼此继承:它们没有继承(两者都从SystemException继承,但这无关紧要)。

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

    最佳答案

    默认情况下,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代替ojit_code:
    throw [System.InvalidOperationException]::new('This is my message')
    

    关于powershell - 在PowerShell中执行错误的catch块执行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50424345/

    10-12 21:12