首先为什么会发生这种情况,为什么异常如此神秘?
您看到的行为似乎是一个错误(在 PowerShell 7.0 中仍然存在;请参阅此 GitHub 问题):
exit
从附加到实例的脚本方法[pscustomobject]
调用- 无论脚本方法是通过间接附加New-Module -AsCustomObject
还是直接通过
Add-Member -MemberType ScriptMethod
-意外失败并出现您看到的神秘错误:错误不是退出,而是作为语句终止错误发出(见下文)和默认情况下继续执行。
在PowerShell 5+class
上定义的方法不受影响。
正确执行此操作的规范方法是什么?我确实想退出并停止所有进一步的执行,而不是恢复控制流。我还需要能够返回退出代码。
通常,从脚本exit
文件的顶级范围调用,而不是从函数内部(无论是否在模块内部)。
请注意,exit
要么立即退出封闭脚本,要么如果在交互式会话中直接调用该函数,则存在整个会话。
如果您的quux()
方法是从脚本中调用的,并且您确实想立即退出封闭脚本,那么PowerShell 4-中唯一的直接解决方法是不要尝试模拟 PowerShell 类并直接定义和调用该quux
函数。
但是,间接的解决方法是对Martin Brandl 的有用答案throw
中的方法进行细微调整:
该解决方案因PowerShell 对终止错误的不一致使用而变得复杂,它分为两类:
这个令人惊讶的区别和 PowerShell 通常令人惊讶的复杂错误处理在此 GitHub 文档问题中进行了描述。
在您的动态模块方法中使用的附加到实例的脚本方法只能生成语句终止错误,而不是脚本终止错误。[pscustomobject]
同样,PowerShell 5+class
方法的行为不同:它们创建脚本终止错误。
要在报告退出代码[1]的同时实现整体终止,您还需要在caller的范围内执行以下操作:
- 捕获/捕获语句终止错误。
- 发出一条
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] 如果脚本由于script -terminating 错误而自动终止,因为基于 - 的解决方案会生成,它的退出代码总是报告为(并打印错误消息),但前提是脚本是通过 PowerShell 的CLI调用的(或);相比之下,在这种情况下未设置交互式PowerShell 会话内部(但会打印错误消息)。
因此,即使 PS 5+ 解决方案是一个选项,您仍可能希望捕获脚本终止错误并将其显式转换为具有特定退出代码的调用,如下所示。
class
1
powershell -file ...
powershell -commmand ...
$LASTEXITCODE
exit