命令脚本退出代码没有被同一行和&&或||?

时间:2015-07-02 00:26:15

标签: windows batch-file cmd

考虑一个名为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是如何表现的。

3 个答案:

答案 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

==>

<强>资源

修改以启发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

  

如果您想强制errorlevel0,那么您可以使用此功能   完全不直观但非常有效的语法:(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