Powershell 无法从Get-PSDrive捕获DriveNotFoundException

Powershell 无法从Get-PSDrive捕获DriveNotFoundException,powershell,Powershell,在以下示例中,我无法捕获由Get PSDrive生成的DriveNotFoundException: try { # Assumes no Q:\ drive connected. $foo = Get-PSDrive -name 'Q' -ErrorAction Stop } catch [System.Management.Automation.DriveNotFoundException] { Write-Output "Drive not found." } ca

在以下示例中,我无法捕获由
Get PSDrive
生成的
DriveNotFoundException

try {
    # Assumes no Q:\ drive connected.
    $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch [System.Management.Automation.DriveNotFoundException] {
    Write-Output "Drive not found."
}
catch {
    Write-Output "Something else went wrong."
}
应打印以下内容:

PS C:\temp> .\foo.ps1
Drive not found.
PS C:\temp>
相反,我得到:

PS C:\temp> .\foo.ps1
Something else went wrong.
PS C:\temp>

如果相关的话,我正在使用Powershell 2.0。

我意识到这是由于一些奇怪的行为(根据上面对Powershell更高版本的评论),但这确实能够处理特定错误:

try {
    $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch {
    if ($Error[0].Exception.GetType().Name -eq 'DriveNotFoundException') {
        Write-Output 'No such drive.'
    }
    else {
        Write-Output 'Something else went wrong.'
    }
}

问题是因为
-ErrorAction Stop
正在更改try/catch块看到的异常类型

您可以通过捕获
ActionPreferenceStopException
类型来证明这一点。因此,让我们运行一些疑难解答代码,看看发生了什么:

try {
    # Assumes no Q:\ drive connected.
    $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch [System.Management.Automation.DriveNotFoundException] {
    Write-Output "Drive not found."
}
catch [System.Management.Automation.ActionPreferenceStopException] {
    Write-Output "Stop Exception."
    write-host "Caught an exception:" -ForegroundColor Red
    write-host "Exception Type: $($_.Exception.GetType().FullName)" -ForegroundColor Red
    write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
}
catch
{
    write-host "Caught an exception:" -ForegroundColor Red
    write-host "Exception Type: $($_.Exception.GetType().FullName)" -ForegroundColor Red
    write-host "Exception Message: $($_.Exception.Message)" -ForegroundColor Red
}
这将返回以下输出:

Stop Exception.
Caught an exception:
Exception Type: System.Management.Automation.DriveNotFoundException
Exception Message: Cannot find drive. A drive with the name 'Q' does not exist.
因此,您可以看到try/catch捕获了
[System.Management.Automation.ActionPreferenceStopException]
异常,即使异常类型是catch块中的
[System.Management.Automation.DriveNotFoundException]

因此,我们可以使用@haliphax解决方案的稍微修改版本来处理它,即检查
ActionPreferenceStopException
catch块中的错误类型:

try {
    # Assumes no Q:\ drive connected.
    $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch [System.Management.Automation.ActionPreferenceStopException] {
    if ($Error[0].Exception.GetType().Name -eq 'DriveNotFoundException') {
        Write-Output "Drive not found."
    }
    else {
        Write-Output "Something else went wrong."
    }
}
catch {
    Write-Output "Something else went wrong."
}

这只是我使用的代码的最终版本的一篇简短文章,基于@HAL9256在中提供的解释:

这已经在PowerShell 2.0和4.0上进行了测试,并且在这两个版本上都有效。我假设在PowerShell 2.0环境中的
Get PSDrive
语句中会发生一些other异常,从而触发
catch
块的风险很小,但在我的用例中,这是一个可接受的风险,稍后会在脚本中触发另一个异常。

补充:

注:以下部分是推测性的。如果我错了,一定要告诉我

  • 观察到的行为是一个可能是PowerShell v1和v2中的一个bug,其中类型为
    [System.Management.Automation.ActionPreferenceStopException]的内部异常
    try/catch
    语句中键入的
    catch
    块的匹配逻辑中意外地屏蔽了原始异常

  • 我怀疑
    [System.Management.Automation.ActionPreferenceStopException]
    纯粹是一个不应该公开的内部异常的原因是
    $Error[0]。异常
    及其在
    catch
    块中的别名,
    $\ucode>异常
    反映了原始异常,即使在PowerShell v1和v2中,
    $Errors
    集合也不包含
    [System.Management.Automation.ActionPreferenceStopException]
    的跟踪

  • 虽然在v3+中修复了错误,但v3+仍然存在,但现在还与键入的
    捕获处理程序中的
    [System.Management.Automation.ActionPreferenceStopException]
    匹配,大概是为了不破坏向后兼容性

  • 鉴于其一般性质(它只是告诉您cmdlet遇到了非终止错误),捕获
    [System.Management.Automation.ActionPreferenceStopException]
    几乎没有什么好处

    • 捕获它的唯一可能的原因是,如果您想知道手头的异常是本机终止的,还是仅由于
      -ErrorAction Stop
      $ErrorActionPreference='Stop'
      而被视为终止的
因此,对于也必须在v2-上运行的代码,我将按如下方式解决问题:

try {
  # Assumes no Q:\ drive connected.
  $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch {  # Use a generic handler to work around the bug in v1 an v2.
  # $_ is the [System.Management.Automation.ErrorRecord] instance representing
  # the PowerShell error at hand (same as $Error[0]).
  # $_.Exception contains the exception that triggered the error,
  # and can be compared to specific exception types with -is.
  if ($_.Exception -is [System.Management.Automation.DriveNotFoundException]) {
    "Drive not found."
  } else {
    "Something else went wrong."
  }
}

这在4.0上适用于我。不知道异常类型在2.0版上是否不同是的,在5.0版中可以正常工作。谢谢。你们都证明了我不是疯子。我看不出这不起作用的任何原因。不幸的是,一些将要运行这个特定脚本的服务器已经安装了2.0,所以我不得不用最小公分母来编写它。感谢您对发生的事情的解释。那么,鉴于编写的原始代码在更高版本的Powershell中正常工作,这是v2特有的吗?我几乎无法控制脚本将在任何机器上运行的版本,但我知道它的范围是2到4。如果在PS的更高版本上,异常被作为
DriveNotFoundException
抛出,我可能需要使用一个裸
catch
来检查错误,因为这样做会让我很痛苦。很棒的侦察。假设
$Error[0].Exception
(或
$\u0.Exception
catch
处理程序中)包含原始异常,即使在PS v2-,您如何确定
[System.Management.Automation.ActionPreferenceStopException]
是匹配的?
try {
  # Assumes no Q:\ drive connected.
  $foo = Get-PSDrive -name 'Q' -ErrorAction Stop
}
catch {  # Use a generic handler to work around the bug in v1 an v2.
  # $_ is the [System.Management.Automation.ErrorRecord] instance representing
  # the PowerShell error at hand (same as $Error[0]).
  # $_.Exception contains the exception that triggered the error,
  # and can be compared to specific exception types with -is.
  if ($_.Exception -is [System.Management.Automation.DriveNotFoundException]) {
    "Drive not found."
  } else {
    "Something else went wrong."
  }
}