【问题标题】:Exit code from a batch file is not propagated to the parent powershell script批处理文件中的退出代码不会传播到父 powershell 脚本
【发布时间】:2019-03-21 21:14:00
【问题描述】:

请注意:

c:\temp\1.cmd

@echo off
setlocal

cmd /c dir aaa
IF %ERRORLEVEL% NEQ 0 GOTO fail
GOTO end
:fail
echo - Script failed

:end
endlocal

现在如果我在命令提示符下运行它:

C:\temp> cmd
Microsoft Windows [Version 10.0.16299.967]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\temp>c:\temp\1.cmd
 Volume in drive C has no label.
 Volume Serial Number is 4A5E-F223

 Directory of C:\temp

File Not Found
- Script failed

C:\temp>echo %errorlevel%
1

C:\temp>

现在我从 Powershell 运行它:

C:\temp> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.16299.967
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.16299.967
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


C:\temp> cmd /c C:\temp\1.cmd
 Volume in drive C has no label.
 Volume Serial Number is 4A5E-F223

 Directory of C:\temp

File Not Found
- Script failed
C:\temp> $LASTEXITCODE
0
C:\temp>

据我所知,退出代码应该正确传播。那么,有什么问题呢?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    注意:在新信息曝光后,此答案已被大幅改写。

    用一些一般指导和背景信息补充jazzdelightsme's effective solution

    • 当从外部cmd.exe调用批处理文件时,例如从PowerShell中,调用它为cmd /c 'file.cmd ... & exit'或 - 如果是字符串需要插值 - 作为
      cmd /c "file.cmd ... & exit",然后需要将 embedded " 转义为 `"[1]。这确保它的行为与从内部 cmd.exe 调用时的退出代码(错误级别)相同,即确保批处理文件的退出代码可靠 em> 成为cmd.exe进程退出代码

      • This post 详细解释了问题和这个 - 晦涩 - 解决方法。
      • 注意:基于call - cmd /c call file.cmd ... 的更简单的解决方法-原则上有效,但在(必要时)会产生不必要的副作用双引号[2]) 带有^ 字符的参数被传递:由于使用call^ 字符(“脱字符号”,严格来说circumflex 重音符号、U+005E)总是加倍,也就是说,参数"a ^ 2" 被批处理文件视为"a ^^ 2" ;目前尚不清楚这种行为的目的是什么,但它似乎一直存在 - 有关 cmd.exe 解析器的详细信息,请参阅 this answer
    • 如果没有& exit 解决方法,只有在确保所有 代码路径以下列方式之一退出并且缺少代码路径是一个容易犯的错误:

      • exit 带有 no 参数,它正确地中继最近执行的命令的退出代码。

        • 警告exit 没有/b 会立即退出整个cmd.exe 实例,因此它不适合在可能以交互方式运行的批处理文件中使用或者必须可以从其他批处理文件中调用并将控制权返回给这些批处理文件。
      • exit /b <code>exit <code>,其中<code> 表示所需的退出代码,即指定显式 退出代码,如jazzdelightsme 的解决方案。

        • 警告:退出带有exit /b 的批处理文件没有明确的<code> 参数不会传递最近执行的命令的退出代码cmd /c <batch-file> ... `& exit 解决方法 - 请参阅 this answer

    其他背景信息,在您的代码的上下文中:

    奇怪的是,在外部调用批处理文件没有& exit(或call,语句如ifgoto、@ 987654357@,endlocal,甚至REM(但奇怪的是,不是::重置cmd.exe稍后报告给0的退出代码 - 即使里面 cmd.exe session %ERRORLEVEL% 设置为通常情况,这意味着此类语句对当前的%ERRORLEVEL% 值没有影响。

    因此:

    • 当您的批处理文件从内部 cmd.exe 运行时,遵循将%ERRORLEVEL% 设置为1 (cmd /c dir aaa) 的命令之后的特定语句,即ifgotoechoendlocal 语句,保留这个值,而%ERRORLEVEL% 在退出批处理文件后以值 1 结束。

      • 奇怪的是,错误级别仅在涉及批处理文件调用的语句之后设置,因此file.cmd || exit 之类的东西起作用,因为||操作员无法将file.cmd 调用识别为失败。这就是解决方法使用& 而不是|| 的原因。
    • 当您的批处理文件从外部 cmd.exe 运行,并且cmd /c "<batch-file> ... & exit"(或cmd /c call <batch-file> ...)调用时,以下相同的语句%ERRORLEVEL%-setting 命令突然重置退出代码,cmd.exe 本身稍后报告给 0,而不是保留后批处理文件执行 %ERRORLEVEL% 值。


    [1] 情况,您可以使用cmd /c <batch-file> ... `& exit,即不用将/c 的参数包含在单个整体字符串中,但是会中断 如果批处理文件路径需要双引号并且至少还存在一个双引号传递参数。

    [2] ^ 字符。在未引用参数中,像往常一样,被解释为cmd.exe转义字符,因此删除

    【讨论】:

      【解决方案2】:

      第一:ERRORLEVEL is not %ERRORLEVEL%

      第二,errorlevel与进程退出码不同。

      尝试如下更改您的 cmd 脚本(注意添加“exit /b 1”):

      @echo off
      setlocal
      
      cmd /c dir aaa
      IF %ERRORLEVEL% NEQ 0 GOTO fail
      GOTO end
      :fail
      echo - Script failed
      exit /b 1
      
      :end
      endlocal
      

      【讨论】:

      • 我会接受它,但它似乎有点奇怪,因为dir确实设置了环境变量%ERRORLEVEL%。可能是除了 ERRORLEVEL,但仍然设置了环境变量。所以,它实际上是退出代码。
      猜你喜欢
      • 1970-01-01
      • 2019-08-08
      • 2011-01-03
      • 2023-01-23
      • 1970-01-01
      • 2019-10-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多