使用嵌套原始错误

时间:2015-06-03 16:51:46

标签: powershell

我是一名C#开发人员,正在尝试使用PowerShell构建有用的东西。这就是为什么我一直试图在PowerShell中使用.NET世界中众所周知的习语。

我正在编写一个具有不同层次抽象的脚本:数据库操作,文件操作等。在某些时候,我想捕获一个错误并将其包装成对最终用户更有意义的东西。这是C#/ Java / C ++代码的常见模式:

Function LowLevelFunction($arg)
{
  # Doing something very useful here!
  # but this operation could throw
  if (!$arg) {throw "Ooops! Can't do this!"}
}

现在,我想调用此函数并包装错误:

Function HighLevelFunction
{
  Try
  {
     LowLevelFunction
  }
  Catch
  {
     throw "HighLevelFunction failed with an error!`nPlease check inner exception for more details!`n$_"
  }
}

这种方法几乎就是我所需要的,因为HighLevelFunction会抛出新错误,原始错误的根本原因会丢失!

在C#代码中,我总是可以抛出新异常并提供原始异常作为内部异常。在这种情况下,HighLevelFunction能够以对客户更有意义的形式传达他们的错误,但仍会提供内部细节以用于诊断目的。

在PowerShell中打印原始异常的唯一方法是使用存储所有异常的$Error变量。这没关系,但是我的脚本用户(我自己现在)应该做更多我想做的事情。

所以问题是:有没有办法在PowerShell中引发异常并提供原始错误作为内部错误?

2 个答案:

答案 0 :(得分:1)

不幸的是,当如 this answer 所述从 catch 块抛出新异常时,脚本堆栈跟踪 (ErrorRecord.ScriptStackTrace) 将重置为 throw 的位置.这意味着内部异常的根源将丢失,使得复杂代码的调试变得更加困难。

有一种替代解决方案,它使用 ErrorRecord.ErrorDetails 定义高级消息并使用 $PSCmdlet.WriteError() 保留脚本堆栈跟踪。它要求将代码编写为 advanced function cmdlet。该解决方案不使用嵌套异常,但仍满足要求“捕获错误并将其包装成对最终用户更有意义的内容”

#------ High-level function ----------------------------------------------

function Start-Foo {
    [CmdletBinding()] param()

    try {
        # Some internal code that throws an exception
        Get-ChildItem ~foo~ -ErrorAction Stop
    }
    catch {
        # Define a more user-friendly error message.
        # This sets ErrorRecord.ErrorDetails.Message
        $_.ErrorDetails = 'Could not start the Foo'

        # Rethrows (if $ErrorActionPreference is 'Stop') or reports the error normally, 
        # preserving $_.ScriptStackTrace.
        $PSCmdlet.WriteError( $_ )
    }
}

#------ Code that uses the high-level function ---------------------------

$DebugPreference = 'Continue'   # Enable the stack trace output

try {
    Start-Foo -ErrorAction Stop
}
catch {
    $ErrorView = 'NormalView'   # to see the original exception info
    Write-Error -ErrorRecord $_
    ''
    Write-Debug "`n--- Script Stack Trace ---`n$($_.ScriptStackTrace)" -Debug
}

输出:

D:\my_temp\ErrorDetailDemo.ps1 : Could not start the Foo
+ CategoryInfo          : ObjectNotFound: (D:\my_temp\~foo~:String) [Write-Error], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,ErrorDetailDemo.ps1

DEBUG:
--- Script Stack Trace ---
at Start-Foo, C:\test\ErrorDetailDemo.ps1: line 5
at , C:\test\ErrorDetailDemo.ps1: line 14

我们的高级错误消息“Could not start the Foo”隐藏了底层异常的错误消息,但不会丢失任何信息(您可以从 catch 处理程序中通过 $_.Exception.Message 访问原始错误消息) .

注意:还有一个字段 ErrorDetails.RecommendedAction,您可以根据需要进行设置。为简单起见,我没有在示例代码中使用它,但您可以像这样设置 $_.ErrorDetails.RecommendedAction = 'Install the Foo'

答案 1 :(得分:0)

您可以在catch块中引发新异常并指定基本异常:

# Function that will throw a System.IO.FileNotFoundExceptiopn
function Fail-Read {
  [System.IO.File]::ReadAllLines( 'C:\nonexistant' )
}

# Try to run the function
try {
  Fail-Read
} catch {
  # Throw a new exception, specifying the inner exception
  throw ( New-Object System.Exception( "New Exception", $_.Exception ) )
}

# Check the exception here, using getBaseException()
$error[0].Exception.getBaseException().GetType().ToString()