使用正确的退出代码报告从 PowerShell 调用批处理文件,同时避免插入符号加倍

Call a batch file from PowerShell with proper exit code reporting while avoiding doubling of carets

我怀疑没有好的解决方案,但也许我忽略了一些东西:

我所追求的是一种方法:


我知道如何解决 (a) 的唯一方法是通过 cmd /c call.

调用批处理文件

不幸的是,这违反了要求 (b),因为使用 call 看似 总是使参数中的 ^ 个字符 加倍。 (相反,not 使用 call 则不会可靠地报告退出代码)。

有没有办法同时满足两个要求?

请注意,PowerShell 只是这里的信使:问题在于 cmd.exe,任何从 cmd.exe 会话外部调用批处理文件的人都会面临同样的问题。


示例(PowerShell 代码):

# Create a (temporary) batch file that echoes its arguments,
# provokes an error, and exits with `exit /b` *without an explicit argument*.
'@echo off & echo [%*] & whoami -nosuch 2>NUL || exit /b' | Set-Content test.cmd

# Invoke the batch file and report the exit code.
.\test.cmd "a ^ 2"; $LASTEXITCODE

输出应该是:

["a ^ 2"]
1

然而,实际上退出代码是不是报告的:

["a ^ 2"]
0          # !! BROKEN

如果我用 cmd /c call .\test.cmd 调用,退出代码是正确的,但 ^ 个字符加倍:

PS> cmd /c call .\test.cmd "a ^ 2"; $LASTEXITCODE
["a ^^ 2"]  # !! BROKEN
1           # OK

我不知道为什么会这样,但确实如此:

cmd /c '.\test.cmd "a ^ 2" & exit'
$LASTEXITCODE

输出:

["a ^ 2"] 
1

感谢beatcracker for finding an effective workaround in ;让我添加一些背景信息和指导:

  • 首先,需要明确的是,不需要解决方法; cmd.exe 的行为显然是一个错误。

  • cmd /c '.\test.cmd "a ^ 2" || exit' - 即 || 而不是 & - 也是一种有效的解决方法。只有 &无条件 对命令进行排序的事实表明,即使 cmd.exe- 内部 失败状态批处理文件的一部分还不知道 作为同一语句的一部分 - 只有 之后 - 这似乎是错误的另一种表现形式。

    • 为什么 explicit exit 调用在批处理文件调用之后作为同一语句的一部分 中继批处理文件的(零或非零)退出代码正确是任何人的猜测,但它似乎有效。
  • 幸运的是,该解决方法对于解决 包含显式 exit /b / [= 的批处理文件中的相关退出代码问题也很有效20=] 调用 - 参见 .

语法注意事项:

  • 在 PowerShell 中,传递 单个 命令字符串的替代方法是传递 单个参数 escape & 字符作为 `&(使用 `,“反引号”,PowerShell 的转义字符)以防止 PowerShell 从解释它(引用 它作为 '&' 也可以):

    cmd /c .\test.cmd "a ^ 2" `& exit
    
  • 不涉及 shell 的环境中,例如从任务计划程序启动时,`- & 的转义 不需要 (也不能使用)。

不必将 entire for-cmd.exe 命令用引号括起来,这样可以更轻松地传递参数 (a) individually 需要双引号并且 (b) 涉及对 PowerShell 变量 and/or 表达式的引用,因为后者需要使用 "..." 而不是 '...' :

# Passing *individual* arguments makes double-quoting easier.
PS> cmd /c .\test.cmd "Version = $($PSVersionTable.PSVersion)" `& exit; $LASTEXITCODE
["Version = 7.2.0-preview.4"]
1

在这种情况下使用 entire for-cmd.exe 命令的引用会很尴尬,因为需要转义特定参数 "字符数:

# Embedded double quotes must now be `-escaped.
PS> cmd /c ".\test.cmd `"Version = $($PSVersionTable.PSVersion)`" & exit"
["Version = 7.2.0-preview.4"]
1

Native module (authored by me; install it from the PowerShell Gallery with Install-Module Native) 附带 函数 ie,其中:

  • 自动应用上述解决方法

  • 通常可以补偿因 PowerShell 向外部程序传递参数中断而引起的问题(请参阅 this answer)。

# After having run Install-Module Native:
# Use of function `ie` applies the workaround behind the scenes.
PS> ie .\test.cmd "Version = $($PSVersionTable.PSVersion)"; $LASTEXITCODE
["Version = 7.2.0-preview.4"]
1

希望 ie 的功能将成为 PowerShell 本身的一部分,作为即将推出的(在 PowerShell v7.2 中)PSNativeCommandArgumentPassing 实验性功能的一部分,该功能旨在作为一个选项-修复了损坏的参数传递 - 参见 GitHub issue #15143