无法从自定义对象中的方法调用exit()

时间:2016-09-26 11:46:45

标签: powershell powershell-module

我试图使用PS模块模拟类,如this回答中所述。但是,在{"实例方法"

exit似乎无法实现。{/ p>
$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. 正确执行此操作的规范方法是什么?我想要退出并停止所有进一步执行,而不是恢复控制流程。我还需要能够返回退出代码。

2 个答案:

答案 0 :(得分:0)

您可以抛出异常并使用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()

答案 1 :(得分:0)

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

您看到的行为似乎是 bug (自PowerShell 7.0起仍然存在;请参见this GitHub issue):

从附加到exit实例的 脚本方法调用[pscustomobject] -脚本方法是否是间接的通过New-Module -AsCustomObject或直接通过
附加 Add-Member -MemberType ScriptMethod-意外失败,并显示了您所看到的神秘错误:该错误不是退出,而是作为声明终止错误(请参见下文)和执行< em>默认情况下继续。

PowerShell 5+ class 上定义的方法 不受影响

正确执行此操作的规范方法是什么?我确实想退出并停止所有进一步的执行,而不是恢复控制流程。我还需要能够返回退出代码。

通常,从{em> script 文件的顶级范围调用exit,而不是从函数内部(无论是否在模块内部)调用。

请注意,exit或者立即退出封闭脚本,或者,如果直接在交互式会话中调用该函数,则存在整个会话。 / p>

如果您的quux()方法是从脚本内调用的,而您确实确实想立即退出该封闭脚本,则在PowerShell 4中使用 direct direct 解决方法 不是试图模拟PowerShell类并直接定义和调用quux函数

但是, 间接间接解决方案是对Martin Brandl's helpful answerthrow方法进行一些细微调整:


PowerShell不一致使用终止错误使解决方案变得复杂,该错误分为两类:

  • 语句-终止错误,该错误(默认情况下)仅终止执行语句,然后继续执行。

  • 脚本终止错误(更准确地说:运行空间终止错误),它们会终止整个执行(脚本及其所有调用者,以及调用PowerShell的CLI的情况(整个Powershell进程)。

this GitHub docs issue中描述了这种令人惊讶的区别以及PowerShell总体上令人惊讶的错误处理。

动态模块方法中使用的附加到[pscustomobject]实例的脚本方法 仅能够生成声明-终止错误,而不是脚本终止错误。

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

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

  • 捕获/捕获语句终止错误。
  • 发出一个exit $n语句作为响应(在脚本作用域内将成功执行),其中$n是所需的退出代码。

您有两个选择:

  • 在调用方的作用域中添加trap语句,该语句会为语句终止和脚本终止错误触发,并调用exit $n,其中$n是所需的退出代码。

    • 要这样做,对Martin答案的唯一调整是将trap { exit }替换为
      trap { exit 1 }报告退出代码1

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

    • 请注意,trap很少使用,但是它可以根据您的情况提供直接的解决方案。

  • 或者,在方法调用前后使用try / catch语句或一组可能引发错误的语句,然后在exit处理程序中调用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 终止错误而终止,那么基于class的解决方案就会生成该脚本,退出代码总是 报告为1(并显示错误消息),但是只有(如果脚本是通过PowerShell的CLI ({{ 1}}或powershell -file ...);相反,在这种情况下,在交互式 PowerShell会话powershell -commmand ...中未设置 (但会显示错误消息)。
因此,即使可以选择使用PS 5+解决方案,您仍然可能想捕获脚本终止错误,并将其显式转换为具有特定退出代码的$LASTEXITCODE调用,如下所示。