我是一名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中引发异常并提供原始错误作为内部错误?
答案 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()