如何解决使用正确的退出代码报告从 PowerShell 调用批处理文件,同时避免插入符号加倍
我怀疑没有好的解决方案,但也许我忽略了一些东西:
我所追求的是一种方式:
-
(a) 从 PowerShell 调用批处理文件,健壮在 PowerShell 的自动
$LASTEXITCODE
变量中反映其(隐式或显式)退出代码。-
值得注意的是,调用以
whoami -nosuch || exit /b
退出的批处理文件应该导致$LASTEXITCODE
反映whoami
的退出代码,即1
。当您从 PowerShell 调用批处理文件(按名称或路径)时,情况并非如此:退出代码是0
(相比之下,在cmd.exe
会话中{{ 1}} 是设置为%ERRORLEVEL%
). -
另请注意,调用应与 PowerShell 的输出流保持集成,因此我不寻找基于
System.Diagnostics.Process
的解决方案。 -
此外,我不了解或控制被调用的批处理文件 - 我正在寻找通用解决方案。
-
-
(b) 没有以任何方式改变传递给批处理文件的双引号参数,也没有以任何方式修改
1
的行为;值得注意的是:-
cmd.exe
个字符应该不加倍(见下文)。 - 使用
^
启用延迟扩展不是一个选项。
-
我知道如何解决 (a) 的唯一方法是通过 /V:ON
调用批处理文件。
不幸的是,这违反了要求 (b),因为在参数中使用 cmd /c call
似乎总是将 call
个字符加倍。 (相反,不使用 ^
则不会可靠地报告退出代码)。
有没有办法同时满足两个要求?
请注意,PowerShell 只是此处的信使:问题在于 call
,任何从 cmd.exe
会话外部调用批处理文件的人都面临同样的问题。
示例(PowerShell 代码):
cmd.exe
输出应该是:
# 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
字符会加倍:
^
解决方法
我不知道为什么会这样,但确实如此:
cmd /c '.\test.cmd "a ^ 2" & exit'
$LASTEXITCODE
输出:
["a ^ 2"]
1
,
感谢 beatcracker 在 his answer 中找到了有效的解决方法;让我补充一些背景信息和指导:
-
首先,需要明确的是,不需要任何解决方法;
cmd.exe
的行为显然是一个错误。 -
cmd /c '.\test.cmd "a ^ 2" || exit'
- 即||
而不是&
- 也是人们期望的有效解决方法。只有&
,无条件 对命令进行排序,这一事实表明即使cmd.exe
-internal 批处理文件的失败状态不是' t 还作为同一声明的一部分 - 仅 之后 - 这似乎是错误的另一种表现形式。- 为什么在批处理文件调用之后的显式
exit
调用作为同一语句的一部分确实中继批处理文件的(零或非零)退出代码正确的是任何人的猜测,但它似乎工作。
- 为什么在批处理文件调用之后的显式
-
幸运的是,该解决方法对于解决不包含显式
exit /b
/exit
调用的批处理文件中的相关退出代码问题也很有效 - 请参阅{ {3}}。
语法注意事项:
-
在 PowerShell 中,传递单个命令字符串的替代方法是传递单个参数并转义{{1 }} 字符作为
&
(使用`&
,“反引号”,PowerShell 的转义字符)以防止 PowerShell 解释它(quoting它作为`
也可以工作):'&'
-
在不涉及 shell 的环境中,例如从 Task Scheduler 启动时,
cmd /c .\test.cmd "a ^ 2" `& exit
的`
转义是 不需要(也不能使用)。
不必将整个 for-&
命令用引号括起来,这样可以更轻松地传递 (a) 单独 需要双引号和 (b ) 涉及对 PowerShell 变量和/或表达式的引用,因为后者需要使用 cmd.exe
而不是 "..."
:
'...'
在这种情况下使用 整个 for-# Passing *individual* arguments makes double-quoting easier.
PS> cmd /c .\test.cmd "Version = $($PSVersionTable.PSVersion)" `& exit; $LASTEXITCODE
["Version = 7.2.0-preview.4"]
1
命令的引用会很尴尬,因为需要转义特定于参数的 cmd.exe
字符。: >
"
this answer 模块(由我编写;使用 # Embedded double quotes must now be `-escaped.
PS> cmd /c ".\test.cmd `"Version = $($PSVersionTable.PSVersion)`" & exit"
["Version = 7.2.0-preview.4"]
1
安装 Native
)带有 功能 Install-Module Native
,其中:
-
自动应用上述解决方法。
-
通常会补偿 PowerShell 向外部程序传递参数时出现的问题(请参阅 from the PowerShell Gallery)。
ie
希望 # 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
的功能将成为 PowerShell 本身的一部分,作为即将推出的(在 PowerShell v7.2 中)ie
实验性功能的一部分,该功能旨在作为选择加入修复损坏的参数传递 - 参见 this answer
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。