3

我正在尝试使用答案中描述的 PS 模块来模拟类。但是,似乎不可能exit从这样的“实例方法”中获得:

$sb =
{
    function bar
    {
        "bar"
    }

    function quux
    {
        exit
    }

    Export-ModuleMember -Function bar,quux
}
$foo = New-Module $sb -AsCustomObject
$foo.bar()
$foo.quux()

结果是

bar
Exception calling "quux" with "0" argument(s): "System error."
At line:17 char:1
+ $foo.quux()
+ ~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ScriptMethodFlowControlException
  1. 首先为什么会发生这种情况,为什么异常如此神秘?
  2. 正确执行此操作的规范方法是什么?我确实想退出并停止所有进一步的执行,而不是恢复控制流。我还需要能够返回退出代码。
4

2 回答 2

1

您可以抛出异常并使用trap它来捕获它并退出脚本:

trap{
    exit
}

$sb =
{
    function bar
    {
        "bar"
    }

    function quux
    {
        throw "Stopping execution"
    }

    Export-ModuleMember -Function bar,quux
}
$foo = New-Module $sb -AsCustomObject
$foo.bar()
$foo.quux()
于 2016-09-26T11:48:45.450 回答
0

首先为什么会发生这种情况,为什么异常如此神秘?

您看到的行为似乎是一个错误(在 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 对终止错误的不一致使用而变得复杂,它分为两类:

  • statement -terminating errors,(默认情况下)仅终止正在执行的语句,然后继续执行。

  • 脚本终止错误(致命错误),它终止整个执行(脚本及其所有调用者,在调用 PowerShell 的 CLI 的情况下,整个 PowerShell 进程)。

这个令人惊讶的区别和 PowerShell 通常令人惊讶的复杂错误处理在此 GitHub 文档问题中进行了描述。

在您的动态模块方法中使用的附加到实例的脚本方法只能生成语句终止错误,而不是脚本终止错误。[pscustomobject]

同样,PowerShell 5+class方法的行为不同:它们创建脚本终止错误

要在报告退出代码[1]的同时实现整体终止,您还需要在caller的范围内执行以下操作:

  • 捕获/捕获语句终止错误。
  • 发出一条exit $n响应语句(在脚本范围的上下文中将成功),其中$n是所需的退出代码。

你有两个选择:

  • 向调用者的作用域添加一个trap语句,该语句会触发语句终止错误和脚本终止错误以及调用exit $n,其中$n是所需的退出代码。

    • 为此,对马丁答案的唯一调整是替换trap { exit }为,例如,
      trap { exit 1 }报告退出代码1

    • throw从ing 函数传达特定的退出代码需要额外的工作 - 见下文。

    • 请注意,trap它很少使用,但它可以在您的情况下提供简单的解决方案。

  • 或者,在方法调用或一组可能引发错误的语句周围使用try/catchexit语句,并在catch处理程序中调用:
    try { $foo.quux() } catch { exit 1 }


设置特定退出代码的解决方法:

# 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+ 解决方案是一个选项,您仍可能希望捕获脚本终止错误并将其显式转换为具有特定退出代码的调用,如下所示。 class1powershell -file ...powershell -commmand ...$LASTEXITCODE
exit

于 2020-03-31T16:19:32.153 回答