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.

+3


source to share


3 answers


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

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

    should 9009

    still be
  • ||

    keeps errorlevel 2 unchanged on error FIND: Invalid switch

    . branch
  • ||

    assessed at call t.cmd 333

    until
  • &&

    branches off in t.cmd 222

    .

In (call )

cm. Response DBenham :

If you want to manually set errorlevel

on 0

, you can use this completely non-intuitive, but very effective syntax (call )

. The space after call

is critical.

If you want to install errorlevel

on 1

, you can use (call)

. It is critical that call

there is no space after .

+5


source


% errorlevel% expands as the line is read. So 123 at the time the line was read comes from the previous command, not t.exe. && is only executed if the current error level (from the t command) is 0.



See setlocal /?

and for more details set /?

.

+3


source


Answer

@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

      

0


source







All Articles