Command script exit code not seen by the same line && or ||?
Consider a script command named t.cmd that consists of only these two lines:
@exit /b 123
@echo If you see this, THEN EXIT FAILED..
So the script just sets the process termination code of the script to 123, but it doesn't kill cmd.exe. The final echo confirms that the output actually causes an immediate return (its output should not appear).
Now execute this script and then print% errorlevel%:
>t.cmd
>echo %errorlevel%
123
So far so good: everything behaves exactly as expected.
But now do all of the above on one line using && for conditional execution:
>t.cmd && echo %errorlevel%
123
I am NOT expecting this: if t.cmd does indeed return code with non-0 code, then it should stop everything after this && (i.e. echo) from executing. The fact that we see the seal means that it is being fulfilled. What's happening?
And if you do all of the above on one line using || for conditional execution:
>t.cmd || echo %errorlevel%
>
This behavior is also the opposite of what I expect (although it is consistent with the & above behavior).
Note that this strange behavior is ONLY true for bat files, not for "raw commands".
Evidence. Consider the following interactions on the command line, where instead of calling t.cmd, I try to execute a fake abcdef command:
>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
Here && and || immediately see the exit code of the failed dummy command.
So why do cmd files behave differently?
A likely related error in cmd.exe is seen in File Redirection in Windows and% errorlevel%
Also, I know that ERRORLEVEL is not% ERRORLEVEL%
By the way, the code above was executed in a 64-bit Win 7 Pro box. I don't know how other Windows versions behave.
source to share
C t.bat
slightly modified as follows:
@exit /b 123%~1
@echo If you see this, THEN EXIT FAILED..
Think about the following output:
==>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
==>
Resources
- (
%~1
etc special page) Command line arguments (options) - (special page) EnableDelayedExpansion
Edit to enlighten 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
==>
Edit 2 to enlighten (or confuse?) &&
And ||
. Save the following piece of code as 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!
Output (from 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
==>
note that
-
||
shows errorlevel 1, although the error'abcd' not recognized
should9009
still be -
||
keeps errorlevel 2 unchanged on errorFIND: Invalid switch
. branch -
||
assessed atcall t.cmd 333
until -
&&
branches off int.cmd 222
.
In (call )
cm. Response DBenham :
If you want to manually set
errorlevel
on0
, you can use this completely non-intuitive, but very effective syntax(call )
. The space aftercall
is critical.If you want to install
errorlevel
on1
, you can use(call)
. It is critical thatcall
there is no space after .
source to share
@JosefZ provides excellent coverage of mismatch between ERRORLEVEL
and exit code regarding command scripts.
Unfortunately, as he noted, &&
and ||
will only work if you invoke the script command with a command call
. In most cases, you would prefer that users can simply run your script command without having to worry about prefixing it every time call
.
Typically, I want my scripts to install both ERRORLEVEL
and an exit code to indicate failure (so that my scripts behave just like regular executables). I used exit /b <nonzero>
to try and do this but had the problem mentioned above.
It turns out that the Windows command interpreter exits with the exit code of the last command executed. The actual exit code of the command exit /b <nonzero>
is ironically 0 (since it exited successfully). It installs ERRORLEVEL
, but not the exit code. Thus, this command will not work. The solution to all of this is to (1) use a command cmd /c exit <nonzero>
and (2) make it run like the last command in the script. Since the command cmd
returns back to the script where the next command is executed, the only way to get it on the last line is to be the last line of your script.
Thus, here's a solution to make everything behave like the one requested by the 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
source to share