首先为什么会发生这种情况,为什么异常如此神秘?
您看到的行为似乎是一个错误(在 PowerShell 7.0 中仍然存在;请参阅this GitHub issue):
从附加到[pscustomobject] 实例的脚本方法调用exit - 脚本方法是否通过@987654322 间接附加@ 或直接通过
Add-Member -MemberType ScriptMethod - 意外失败,出现您看到的神秘错误:错误不是退出,而是作为 语句 终止错误发出(请参阅下面)并默认执行继续。
PowerShell 5+ class 上定义的方法不受到影响。
正确执行此操作的规范方法是什么?我确实想退出并停止所有进一步的执行,而不是恢复控制流。我还需要能够返回退出代码。
通常,exit 是从 脚本 文件的顶级范围调用的,而不是从函数内部(无论是否在模块内部)。
请注意,exit 要么立即退出封闭脚本,要么,如果在交互式会话中直接调用该函数,存在整个会话。
如果您的 quux() 方法是从脚本中调用的,并且您确实想立即退出封闭脚本,则在 PowerShell 4- 中唯一的直接解决方法是不尝试模拟 PowerShell 类并直接定义和调用quux 函数。
但是,间接解决方法是从Martin Brandl's helpful answer 对throw 方法添加一个小调整:
解决方案因 PowerShell 对终止错误的不一致使用而变得复杂,分为两类:
this GitHub docs issue 中描述了这种令人惊讶的区别和 PowerShell 令人惊讶的复杂错误处理。
附加到[pscustomobject] 实例的脚本方法(如在您的动态模块方法中使用的那样)只能生成语句终止错误,不是脚本终止的。
同样,PowerShell 5+ class 方法 的行为不同:它们创建 脚本-终止错误。
要实现整体终止同时还报告退出代码[1],您还需要在中执行以下操作调用者的范围:
- 捕获/捕获语句终止错误。
- 发出
exit $n 语句作为响应(在脚本范围的上下文中会成功),其中$n 是所需的退出代码。
你有两个选择:
设置特定退出代码的解决方法:
# Create a custom object with methods,
# via a dynamic module.
$foo = New-Module -AsCustomObject {
function bar
{
"bar"
}
function quux
{
# Throw a statement-terminating error with the desired exit code,
# to be handled by the `trap` statement in the caller's scope.
throw 5
}
}
# Set up the trap statement for any terminating error.
# Note: Normally, you'd place the `trap` statement at the
# beginning of the script.
trap {
# Get the exit code from the most recent error or default to 1.
# Note: $_ is a *wrapper* error record arund the original error record
# created with `throw`; however, the wrapper's .ToString() representation
# is the (string representation of) the original object thrown.
if (-not ($exitCode = $_.ToString() -as [int])) { $exitCode = 1 }
exit $exitCode
}
$foo.bar()
$foo.quux() # This triggers the trap.
# Getting here means that the script will exit with exit code 0
# (assuming no other terminating error occurs in the remaining code).
[1] 如果脚本自动由于 脚本 终止错误而终止,因为基于class 的解决方案会生成它的退出代码总是报告为1(并打印错误消息),但只有如果脚本是通过PowerShell的CLI调用(powershell -file ...或powershell -commmand ...) ;相比之下,在 interactive PowerShell 会话中 $LASTEXITCODE 在这种情况下没有设置(但会打印错误消息)。
因此,即使 PS 5+ 解决方案是一个选项,您可能仍希望捕获脚本终止错误并将其显式转换为具有特定退出代码的 exit 调用,如下所示。