考虑一个名为t.cmd的命令脚本,该脚本仅包含以下两行:
@exit /b 123
@echo If you see this, THEN EXIT FAILED..
因此,脚本只是将脚本执行过程的退出代码设置为123,但不会终止cmd.exe。最后的回声确认退出实际上导致立即返回(其输出不应出现)。
现在执行此脚本,然后打印出%errorlevel%:
>t.cmd
>echo %errorlevel%
123
到目前为止一切顺利:一切都与预期完全一样。
但现在使用&&&&条件执行:
>t.cmd && echo %errorlevel%
123
我不指望这样:如果t.cmd确实以非0退出代码返回,那么它应该在那之后停止所有&& (即回声)来自执行。我们看到它打印的事实意味着它DID执行。到底是怎么回事?
如果在一行上执行上述所有操作,请使用||条件执行:
>t.cmd || echo %errorlevel%
>
这种行为也与我的预期相反(尽管它与上面的&&行为一致)。
请注意,这种奇怪的行为仅适用于bat文件,但不适用于“原始命令”。
证明:考虑以下命令行交互,而不是调用t.cmd,我尝试执行bogus命令abcdef:
>abcdef
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
>echo %errorlevel%
9009
>abcdef && echo %errorlevel%
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
>abcdef || echo %errorlevel%
'abcdef' is not recognized as an internal or external command,
operable program or batch file.
9009
在这里,&&和||立即查看失败的bogus命令的退出代码。
那么为什么cmd文件的行为会有所不同?
在File redirection in Windows and %errorlevel%
中观察到cmd.exe中可能存在相关错误另外,我知道ERRORLEVEL is not %ERRORLEVEL%
顺便说一句,上面的代码都是在Win 7 Pro 64位框上执行的。我不知道其他版本的Windows是如何表现的。
答案 0 :(得分:5)
t.bat
略微修改如下:
@exit /b 123%~1
@echo If you see this, THEN EXIT FAILED..
想出下一个输出:
==>t.bat 1
==>echo %errorlevel%
1231
==>t.bat 2&echo %errorlevel%
1231
==>echo %errorlevel%
1232
==>cmd /V /C t.bat 3^&echo !errorlevel!
1233
==>echo %errorlevel%
0
==>cmd /V /C t.bat 4^&echo !errorlevel!^&exit /B !errorlevel!
1234
==>echo %errorlevel%
1234
==>
<强>资源强>
%~1
等特殊页面)Command Line arguments (Parameters) 修改以启发EnableDelayedExpansion
:
==>cmd /v
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
==>t.bat 5&echo !errorlevel!
1235
==>echo %errorlevel%
1235
==>
编辑2 以启发(或混淆?)&&
和||
。将下一个代码段保存为errlevels.cmd
:
@ECHO ON >NUL
@SETLOCAL enableextensions enabledelayedexpansion
(call )
@echo ^(call ^) command clears errorlevel %errorlevel%
abcd /G>NUL 2>&1
@echo abcd /G: "'abcd' not recognized" errorlevel %errorlevel%
abcd /G>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
@echo abcd /G: ^|^| changed errorlevel %errorlevel%
find /G >NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
@echo find /G: ^|^| unchanged errorlevel %errorlevel%
call t.cmd 333 && echo YES !errorlevel! || echo NO !errorlevel!
type t.cmd
t.cmd 222 && echo YES !errorlevel! || echo NO !errorlevel!
输出(来自errlevels.cmd
):
==>errlevels.cmd
==>(call )
(call ) command clears errorlevel 0
==>abcd /G 1>NUL 2>&1
abcd /G: "'abcd' not recognized" errorlevel 9009
==>abcd /G 1>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
NO 1
abcd /G: || changed errorlevel 1
==>find /G 1>NUL 2>&1 && echo YES !errorlevel! || echo NO !errorlevel!
NO 2
find /G: || unchanged errorlevel 2
==>call t.cmd 333 && echo YES !errorlevel! || echo NO !errorlevel!
NO 333
==>type t.cmd
@exit /B %~1
==>t.cmd 222 && echo YES !errorlevel! || echo NO !errorlevel!
YES 222
==>
请注意
||
显示错误级别1,但'abcd' not recognized
错误应为9009
,而||
使FIND: Invalid switch
错误的错误级别2保持不变。||
分支在call t.cmd 333
期间进行评估&&
分支在t.cmd 222
评估。在(call )
点击DBenham's answer:
如果您想强制
errorlevel
到0
,那么您可以使用此功能 完全不直观但非常有效的语法:(call )
。空间call
之后至关重要。如果您想将
errorlevel
设置为1
,则可以使用(call)
。它 至关重要的是call
之后没有任何空格。
答案 1 :(得分:4)
%errorlevel%被展开。因此,当读取行时它是123,因此来自上一个命令而不是t.exe。 &安培;&安培;仅在当前errorlevel(来自t命令)为0时执行。
有关详细信息,请参阅setlocal /?
和set /?
。
答案 2 :(得分:0)
@ JosefZ的回复提供了关于命令脚本ERRORLEVEL
和退出代码之间差异的绝佳报道。
不幸的是,正如他所指出的那样,只有在使用&&
命令调用命令脚本时,||
和call
运算符才会起作用。在大多数情况下,您希望用户只需运行命令脚本,而不必记住每次都以call
为前缀。
通常,我希望我的脚本设置ERRORLEVEL
和退出代码以指示失败(为了使我的脚本与常规可执行文件的行为相同)。我曾经使用exit /b <nonzero>
来尝试这样做,但是上面已经出现了问题。
事实证明,Windows命令解释程序以执行的最后一个命令的退出代码退出。具有讽刺意味的是,exit /b <nonzero>
命令的实际退出代码为0(因为它已成功退出)。它 设置ERRORLEVEL
,但不设置退出代码。因此,该命令将不起作用。所有这些的解决方案是(1)使用cmd /c exit <nonzero>
命令,以及(2)使其作为脚本中的最后一个命令执行。由于cmd
命令返回到脚本,然后执行下一个命令,将其作为最后一行执行的唯一方法是让它成为脚本的最后一行。
因此,这是一个让所有事情都像OP要求的行为的解决方案:
@echo off & setlocal
if "%1" equ "fail" (
echo -- Failure
goto fail
) else (
echo -- Success
exit /b 0
)
:fail
@REM // Exit with script a failure exit code.
cmd /c exit 37