之间的差异|||退出/b“和"||退出/b!errorlevel!" [英] Difference between "|| exit /b" and "|| exit /b !errorlevel!"
问题描述
我们有一堆 .bat
构建脚本,这些脚本由基于PowerShell的GitLab运行程序调用,最近从以下代码中进行了重构:
We have a bunch of .bat
build scripts which are invoked by a PowerShell based GitLab runner that were recently refactored from:
program args
if !errorlevel! neq 0 exit /b !errorlevel!
更简洁:
program args || exit /b
今天,我调查了一个构建作业,如果您查看错误日志,该构建作业显然会失败,但是据报告是成功的.经过大量实验后,我发现此模式始终无法按预期运行:
Today I investigated a build job which obviously failed if you looked at the error log but which was reported as a success. After much experimentation I discovered that this pattern didn't always work as expected:
program args || exit /b
但是这似乎在前者不起作用时起作用:
but this did appear to work when the former didn't:
program args || exit /b !errorlevel!
我已经阅读了这样的问题> Windows批处理退出选项b带有或不带有错误级别,以及下面来自 https://www.robvanderwoude.com/exit的声明.php ,但仍然无法完全解释我正在观察的内容.
I've read the SO question Windows batch exit option b with or without errorlevel and the statement below from https://www.robvanderwoude.com/exit.php but still can't quite explain what I'm observing.
DOS联机帮助(HELP EXIT)不能清楚地表明/B参数退出脚本的当前实例,而不必等同于退出当前脚本.IE.如果脚本是在CALLed代码段中,则EXIT/B退出CALL,而不是脚本.
The DOS online help (HELP EXIT) doesn't make it clear that the /B parameter exits the current instance of script which is not necessarily the same as exiting the current script. I.e. if the script is in a CALLed piece of code, the EXIT /B exits the CALL, not the script.
这是我用来浏览此文件的最小批处理文件:
This is the minimal batch file I used to explore this:
@echo off
setlocal EnableDelayedExpansion
cmd /c "exit 99" || exit /b
:: cmd /c "exit 99" || exit /b !errorlevel!
这就是我调用批处理文件的方式(以模拟基于GitLab PowerShell的运行程序如何调用它):
And this is how I invoked the batch file (to simulate how it was invoked by the GitLab PowerShell based runner):
& .\test.bat; $LastExitCode
以下是输出,具体取决于执行批处理文件中的两行:
Here is the output depending on which of the two lines in the batch file is executed:
PS> & .\test.bat; $LastExitCode
0
PS> & .\test.bat; $LastExitCode
99
还有一种获取正确行为的方法,即使用 CALL
在PowerShell中长期调用批处理文件:
There is another way to get the correct behaviour which is to invoke the batch file longhand from within PowerShell using CALL
as well:
PS> & cmd.exe /c "call .\test.bat"; $LastExitCode
99
虽然我很欣赏这可能是从PowerShell调用批处理文件的正确方法,但根据我所看到的许多示例,这似乎不是常识.我也想知道,如果PowerShell是正确的方式",为什么它不以这种方式调用批处理文件.最后,我仍然不明白为什么,当放弃 call
时,行为会根据是否将!errorlevel!
添加到 exit/b
语句.
While I appreciate that this may be the correct way to invoke a batch file from PowerShell, that does not appear to be common knowledge based on the many examples I've seen. Also I wonder why PowerShell doesn't invoke a batch file this way if it's "the right way". Lastly I still don't understand why, when leaving off the call
, the behaviour changes depending on whether we add the !errorlevel!
to the exit /b
statement.
更新:到目前为止,感谢您进行的所有讨论,但我感到迷失在草丛中,这可能是我对原始问题过于含糊的错.我认为(如果可能的话),我真正想得到的是确定性声明,有关何时(应该)在以下声明中评估错误级别
:
UPDATE: Thanks for all the discussion so far but I feel it's getting lost in the weeds which is probably my fault for being too vague in my original question. What I think I'm really after (if possible) is a definitive statement about when the errorlevel
is (supposedly) evaluated in the following statement:
program || exit /b
它是否真的像这样早评估了(即在运行 program
之前):
Is it really evaluated early (i.e. before program
is run) like this:
program || exit /b %errorlevel%
还是懒惰地评估(即在运行 program
之后并且内部已更新 errorlevel
之后执行 exit
时),更相似对此:
Or is it evaluated lazily (i.e. when exit
is being executed after program
has run and internally errorlevel
has been updated), more analogous to this:
program || exit /b !errorlevel!
因此,我并不是很在乎猜测,除非可悲的是,这是我们可以做的最好的,在这种情况下,知道没有明确的答案或它是一个错误是我可以接受的答案:o).
Hence I'm not really after speculation unless, sadly, that is the best that we can do in which case knowing there is no definitive answer or that it's a bug is an acceptable answer to me :o).
推荐答案
解决方法:
-
通过
cmd/c< batch-file>调用您的批处理文件...`&退出
,在这种情况下为||没有显式退出代码的退出/b
解决方案 可以正常工作.
Call your batch file via
cmd /c <batch-file> ... `& exit
, in which case the|| exit /b
solution without an explicit exit code works as expected.
-
cmd/c.\ test.bat`&退出
- 请注意,使用
`
进行转义的&
,可以防止PowerShell预先解释&
.如果要在不涉及外壳的环境中运行命令,请省略`
,例如计划任务. - 或者,您可以使用
cmd/c< batch-file>...&退出"
,但是需要转义作为参数一部分的任何"
字符.如果没有使用PowerShell变量或表达式,则可以选择单引号,这样可以避免该问题:cmd/c'< batch-file>...&退出"
- Note the
`
-escaped&
, which prevents PowerShell from interpreting&
up front. Omit the`
if you're running the command from an environment that doesn't involve a shell, such as from aa scheduled task. - Alternatively, you can use the form
cmd /c "<batch-file> ... & exit"
, but that requires escaping of any"
characters that are part of arguments. If no PowerShell variables or expressions are being used, single-quoting is an option, which avoids that problem:cmd /c '<batch-file> ... & exit'
使用 cmd/c< batch-file>...`&建议退出
定期从外部 cmd.exe
调用批处理文件,因为即使批处理文件没有显式 exit/b
(或 exit
)调用可能会出现异常情况-请参见此答案.
Using cmd /c <batch-file> ... `& exit
routinely to call batch files from outside cmd.exe
is advisable, as even batch files without explicit exit /b
(or exit
) calls can otherwise behave unexpectedly - see this answer.
或者-但仅当您的批处理文件永远不需要从另一个批处理文件中调用 时,才应将控件返回到该文件,并且它永远不需要成为 cmd的一部分/c
multi -不是 last 命令的命令行 [1] -您可以使用||退出
,而不是 ||exit/b
-这会立即整体退出正在执行的 cmd.exe
进程 ,但是退出代码(错误级别)为 然后从外部 cmd.exe调用 direct 直接可靠地报告em(至少在
,例如< command> || exit
语句的上下文中)&.\ test.bat
(或者在这种简单情况下,只需.\ test.bat
).
Alternatively - but only if your batch file never needs to be called from another batch file to which control should be returned and if it never needs to be part of a cmd /c
multi-command command line where it isn't the last command[1] - you can use || exit
instead of || exit /b
- this exits the executing cmd.exe
process as a whole, instantly, but the exit code (error level) is then reliably reported (at least in the context of a <command> || exit
statement) also with direct invocation from outside cmd.exe
, such as & .\test.bat
(or, in this simple case, just .\test.bat
) from PowerShell.
同时将 setlocal EnableDelayedExpansion
与 exit/b!ERRORLEVEL!
结合使用时-由于使用了 explicit 退出代码-显然更多麻烦并且可能会有副作用,特别是从 echo hi!
之类的命令中悄悄地删除!
字符(尽管可以通过放置 setlocal EnableDelayedExpansion
在 exit/b
调用之前的行上调用,如果有多个退出点,则需要重复.)
While combining setlocal EnableDelayedExpansion
with exit /b !ERRORLEVEL!
works too - due to using an explicit exit code - it is obviously more cumbersome and can have side effects, notably quietly removing !
characters from commands such as echo hi!
(while it's possible to minimize that problem by placing the setlocal EnableDelayedExpansion
call on the line just before an exit /b
call, that would require duplication if there are multiple exit points).
cmd.exe
的行为很不幸,但无法避免.
cmd.exe
's behavior is unfortunate in this case, but can't be avoided.
从 cmd.exe
外部调用批处理文件时:
When calling a batch file from outside cmd.exe
:
-
exit/b
-不带退出代码(错误级别)参数-仅设置cmd.exe 代码>预期的处理退出代码-即批处理文件中最近执行的命令的退出代码-如果跟踪批处理文件调用,则
,即&退出
cmd/c< batch-file>...`&退出
exit /b
- without an exit-code (error-level) argument - only sets thecmd.exe
process exit code as expected - namely to the exit code of the most recently executed command in the batch file - if you follow the batch-file call with& exit
, i.e. ascmd /c <batch-file> ... `& exit
-
没有
&退出
解决方法,来自批处理文件的无参数exit/b
调用反映在%ERRORLEVEL%
变量 intra-cmd.exe
-session ,但这不会转换为cmd.exe
的 process 退出代码,然后默认为0
. [1]
Without the
& exit
workaround, an argument-lessexit /b
call from a batch file is reflected in the%ERRORLEVEL%
variable intra-cmd.exe
-session, but that doesn't translate tocmd.exe
's process exit code, which then defaults to0
.[1]
使用 &exit
解决方法,批处理文件内无参数的 exit/b
确实正确设置了 cmd.exe
的退出代码,即使在< command>中||exit/b
语句,在这种情况下,将按预期中继< command>
的退出代码.
With the & exit
workaround, intra-batch-file argument-less exit /b
does properly set cmd.exe
's exit code, even in a <command> || exit /b
statement, in which case <command>
's exit code is relayed, as intended.
退出/b< code>
,即明确传递退出代码< code>
明确,始终有效 [2] ,即&这样就不需要 退出
.
exit /b <code>
, i.e. passing an exit code <code>
explicitly, always works[2], i.e. the & exit
workaround is then not needed.
这种区别是一种模糊的不一致,可以合理地称为错误; Jeb的有用答案包含示例代码,演示了该行为(使用不太全面的 cmd/c调用...
解决方法,但它同样适用于 cmd/c"...& exit"
).
This distinction is an obscure inconsistency that could justifiably be called a bug; Jeb's helpful answer has sample code that demonstrates the behavior (using the less comprehensive cmd /c call ...
workaround as of this writing, but it applies equally to cmd /c "... & exit"
).
[1]使用 cmd/c
,您可以传递多个语句来执行,而 last 语句确定 cmd.exe
进程的退出代码.例如 cmd/c"ver&dir nosuch"
报告退出代码 1
,因为不存在的文件系统项 nosuch
导致 dir
设置错误级别到 1
,无论前面的命令( ver
)是否成功.不一致之处是,对于名为 test.bat
的批处理文件,该文件以 exit/b
退出而没有显式的退出代码参数, cmd/c test.bat
始终报告 0
,而 cmd/c test.bat`&exit
正确报告在批处理文件退出之前执行的 last语句的退出代码.
[1] With cmd /c
, you can pass multiple statements for execution, and it is the last statement that determines the cmd.exe
process' exit code. E.g, cmd /c "ver & dir nosuch"
reports exit code 1
, because the non-existent file-system item nosuch
caused dir
to set the error level to 1
, irrespective of whether or not the preceding command (ver
) succeeded. The inconsistency is that, for a batch file named test.bat
which exits with exit /b
without an explicit exit-code argument, cmd /c test.bat
always reports 0
, whereas cmd /c test.bat `& exit
properly reports the exit code of the last statement executed before the batch file exited.
[2]可以直接指定退出代码,也可以通过变量指定退出代码,但陷阱是-由于 cmd.exe
的前期变量扩展-< command>||exit/b%ERRORLEVEL%
不能按预期工作,因为此时%ERRORLEVEL%
扩展到错误级别 prior 此语句,而不是< command>
所设置的语句;这就是为什么通过运行 setlocal enabledelayedexpansion
或使用/V
调用 cmd.exe
来延迟扩展的原因选项,在这种情况下是必需的:< command>||退出/b!ERRORLEVEL!
[2] The exit code may be specified literally or via a variable, but the pitfall is that - due to cmd.exe
's up-front variable expansion - <command> || exit /b %ERRORLEVEL%
does not work as intended, because %ERRORLEVEL%
at that point expands to the error level prior to this statement, not to the one set by <command>
; this is why delayed expansion, via having run setlocal enabledelayedexpansion
or having invoked the cmd.exe
with the /V
option, is necessary in this case: <command> || exit /b !ERRORLEVEL!
这篇关于之间的差异|||退出/b“和"||退出/b!errorlevel!"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!