【问题标题】:PowerShell and process exit codesPowerShell 和进程退出代码
【发布时间】:2019-12-19 11:11:10
【问题描述】:

这个自我回答的问题试图解决在 PowerShell 中处理 process exit codes 的两个不同方面:

  • 在 PowerShell 代码中,如何查询外部进程设置的退出代码(对外部程序的调用),这些退出代码如何与 PowerShell 的错误处理集成?

  • 当其他人通过其 CLI、pwsh(PowerShell 核心)/powershell.exe(Windows PowerShell)调用 PowerShell 时,决定 PowerShell 进程的退出代码的因素将成功与失败传达给调用进程(例如,它可以是构建 / CI / 自动化服务器任务、计划任务或不同的 shell)。

【问题讨论】:

    标签: powershell error-handling exit-code


    【解决方案1】:

    当前为 PowerShell [Core] 7.2.1

    PowerShell-内部使用退出代码:

    PowerShell-内部,本地 PowerShell 命令通常在进程内运行,从运行子进程退出代码>外部程序发挥的作用非常有限

    • 本机 PowerShell 命令通常不设置退出代码,也不对其进行操作。

    • PowerShell 有一个抽象对应来退出代码automatic, Boolean success-status variable $?:

    • 它反映了最近执行的命令是否有任何错误,但在实践中它很少使用,尤其是因为 - 直到版本 6.x - 一些看似无关紧要的东西,例如在 (...) 中包含命令重置 $?$true - 见GitHub issue #3359 - 因为在用户函数中使用Write-Error 不会将$? 设置为$false - 见GitHub issue #3629;但是,最终会providing the ability for user code to set $? explicitly has been green-lit 用于未来的版本。

    • 虽然$? 还反映(立即)外部程序是否报告了退出代码0(信号成功,使$? 报告$true)或非零退出代码(通常表示失败,使$? $false),它是自动$LASTEXICODE变量,它包含特定退出代码作为一个整数 em>,并且在同一会话中调用另一个外部程序(如果有)之前,该值将一直保留。

      • 警告:由于 cmd.exe 的怪癖,批处理文件的退出代码未可靠报告,但您可以解决此问题cmd /c <batch-file> ... `& exit - 见this answerGitHub issue #15143 还建议将此解决方法构建到 PowerShell 本身中。
      • 此外,在 v7.1 之前,如果外部程序报告退出代码 0$? 可以报告误报,同时还产生 stderr 输出,并且还有涉及 2> 或 @987654355 的 PowerShell 重定向@ - 见this answerGitHub issue #3996;从 PowerShell PowerShell Core 7.2.0-preview.4 开始;更正后的行为以 experimental feature PSNotApplyErrorActionToStderr 的形式提供。
    • 与 PowerShell 原生命令报告的终止错误或非终止错误不同,来自外部程序的非零退出代码不能$ErrorActionPreference 首选项变量自动执行;也就是说,您不能使用该变量来使外部程序的 stderr 输出静音,更重要的是,当外部程序报告非零退出代码时,您也不能通过值 'Stop' 选择中止脚本。

      • RFC #277 提出了将外部程序更好地集成到 PowerShell 的错误处理中。

    当 PowerShell 从外部调用 时,如何控制 PowerShell 报告为 its 退出代码的内容:

    设置一个退出代码,至少传达成功(0)与失败(通常为非零)是外部调用者的重要机制了解您的 PowerShell 代码是否总体成功,例如从计划任务或通过 PowerShell CLI(命令行界面)从 Jenkins 等自动化服务器调用时 - @ 987654331@ 用于 PowerShell [Core] 与 powershell.exe 用于 Windows PowerShell。

    CLI 提供了两种执行 PowerShell 代码的方式,您可以使用exit <n> 设置退出代码,其中<n> 是所需的退出代码:

    • -File <script> [args...] 期望执行 脚本文件 (*.ps1) 的路径,可以选择后跟参数。

      • 在这样的脚本文件中执行exit <n>直接(而不是在您调用该脚本的另一个脚本中)使PowerShell进程将其退出代码报告为@987654367 @。

      • 如果给定的脚本文件隐式退出或仅带有exit(没有退出代码参数),则会报告退出代码0

    • -Command <powershell-code> 需要一个包含一个或多个 PowerShell 命令的字符串。

      • 为安全起见,请使用exit <n> 作为该命令字符串的直接部分 - 通常作为最后一条语句。

    如果您的代码是从通过退出代码检查成功的工具调用的,请确保所有代码路径都明确使用exit <n> 来终止。

    警告:如果 PowerShell 进程由于 未处理的脚本终止错误而终止 - 无论 CLI 是使用 -File 还是 -Command 调用的 - 退出代码始终为1

    • 脚本终止(致命)错误是通过使用 throw 语句从 PowerShell 代码生成的,或者通过使用
      -ErrorAction Stop$ErrorActionPreference = 'Stop' 升级不太严重的本机 PowerShell 错误,或者通过按 Ctrl-C 强制终止脚本。

    • 如果退出代码1 不够具体(通常,因为通常只需要传达成功与失败),您可以将您的代码包装在try / catch 语句中,并使用catch 块中的exit <n>

    PowerShell 如何设置其进程退出代码的确切规则很复杂;在下面找到摘要。


    PowerShell 如何设置其进程退出代码:

    • 如果发生未处理的脚本终止错误,退出代码始终为1

    • 使用-File,执行脚本文件 (*.ps1):

      • 如果脚本直接执行exit <n>,则<n>成为退出代码(嵌套调用中的此类语句是无效)。

      • 否则是0即使在脚本执行过程中发生了非终止或语句终止错误

    • 使用 -Command,执行包含一个或多个语句的命令字符串:

      • 如果 exit <n> 语句作为命令字符串中传递的语句之一直接执行(通常是 last 语句) , <n> 成为退出代码。

      • 否则,由 last 执行的语句的成功状态,如 $? 所暗示的那样,决定了退出代码:

        • 如果$? 是:

          • $true -> 退出代码 0
          • $false -> 退出代码 1 - 即使最后执行的语句是报告了不同非零退出代码的外部程序。
        • 鉴于您的命令字符串中的 last 语句可能不是您想要指示其成功与失败的语句,明确使用 exit <n> 以可靠地控制退出代码,它还允许您报告特定非零退出代码。

          • 例如,要忠实地传递外部程序报告的退出代码,请将; exit $LASTEXITCODE 附加到您传递给-Command 的字符串中。

    PowerShell 7.0 的不一致和缺陷:

    • 可以说,-Command (-c) 应该报告最后一条语句的特定退出代码 - 如果它有一个 - 而不是抽象的 01。例如,pwsh -c 'findstr'; $LASTEXITCODE 应该报告2findstr.exe 的特定退出代码,而不是抽象的1 - 请参阅GitHub issue #13501

    • 带有*.ps1 文件/-File CLI 参数的退出代码报告:

      • 这只是一个显式的exit <n> 语句,有意义地设置了一个退出代码;相反,它应该再次是在脚本中执行的确定退出代码的 last 语句(当然,可以是 exit 语句),就像在 POSIX 兼容的 shell 和使用-Command,尽管是以次优方式讨论的。

      • 当您通过-File 调用*.ps1 脚本或通过-Command 作为最后一条语句时,在没有通过exit 语句退出的脚本的情况下,PowerShell 的退出代码为始终为0 (除了特殊的 Ctrl-C / throw 情况,它变成1)。

      • 相比之下,当调用 in-session 时,同样在没有 exit 的情况下,$LASTEXICODE 反映了任何外部程序的退出代码(或其他 *.ps1 if 它设置了一个退出代码)最后执行 - 无论是在脚本内执行还是在 之前

      • 换句话说:

        • -File-Command 不同,在没有exit 语句的情况下,退出代码被明确设置为0(除非异常终止)。
        • 在会话中,在没有 @987654430 的情况下,根本没有为整个脚本设置退出代码(如 $LASTEXITCODE 所示) @ 声明。
      • GitHub issue #11712

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-09
      • 2017-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多