9

The first premise is that batch scripts will execute all commands in them, even if one fails.

@echo off
does_not_exist.exe
also_does_not_exist.exe
C:\Users\user>two_failures.bat
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.
'also_does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.

This necessitates the following error checking.

@echo off

does_not_exist.exe

if %errorlevel% neq 0 (
    exit /b %errorlevel%
)

also_does_not_exist.exe

if %errorlevel% neq 0 (
    exit /b %errorlevel%
)
C:\Users\user>error_check.bat
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.

C:\Users\user>echo %errorlevel%
9009

The second premise is command chaining. On Windows you use && to run a second command only if the first succeeds.

C:\Users\user>does_not_exist.exe && echo Second Command!
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.

Command chaining does not short circuit when a batch script exits with a non-zero return code.

C:\Users\user>error_check.bat && echo Second Command!
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.
Second Command!

Why is this the case?

5
  • 3
    What a splendid first question! Kudos!
    – Matthias
    Commented May 19 at 7:52
  • 1
    It looks like you are quite advanced with Windows batch file but it has some VERY significant limitations. I would really go learn PowerShell and try to move away from anything that's more than a couple simple actions. You can easily load PowerShell from BATCH files so the end-users aren't even going to notice the difference when made properly.
    – Nelson
    Commented May 20 at 8:24
  • Back in the days of DOS, COMMAND.COM couldn't run more than once batch file at once; if you invoked one batch file from another, COMMAND.COM would forget about the calling batch file. The workaround at the time was to spawn another copy of COMMAND.COM to run the second batch file. CMD.EXE operates in this mode by default for compatibility, but it also provides CALL which can nest batch files (among other things). I assume you're tripping over the compatibility mode when you're trying to use batch files in a short-circuit without the use of CALL.
    – Neil
    Commented May 20 at 18:37
  • 1
    @Neil Later versions of COMMAND.COM (at least the one in MS DOS 6.22, where I learned to code) also already had CALL. Commented May 23 at 0:21
  • @PaŭloEbermann That's interesting; I learned with MS DOS 3.x which I don't remember having it. I wonder whether support was added due to the development of Windows NT or maybe OS/2?
    – Neil
    Commented May 23 at 5:03

3 Answers 3

5

Command chaining does not short circuit when a batch script exits with a non-zero return code.

The reason it does not work is that the entire command line/block has already been parsed by the command interpreter. See later for more information.

For this to work as expected, you need to use one of the following:

call error_check.bat && echo this
cmd /c error_check.bat && echo this

Example:

F:\test>call error_check.bat && echo this
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.

F:\test>cmd /c error_check.bat && echo this
'does_not_exist.exe' is not recognized as an internal or external command,
operable program or batch file.

The first one is slightly faster as it is executed by the current shell. The second one start (and exits) a new shell.

The reason this happens is explained nicely in this answer, Error codes seemingly ignored by '&&' on Windows command prompt, by aschipfl:

When running test.bat is involved in a command line where multiple commands are combined (using the concatenation operator &, the conditional ones && and ||, or even a block of code within parentheses ()), all the commands following test.bat are executed even if call was not used. This is because the entire command line/block has already been parsed by the command interpreter.

However, when call is used, the ErrorLevel value returned by the batch file is received.

(emphasis mine)

See the above link for a more complete discussion.

3
  • Great answer. +1
    – LPChip
    Commented May 19 at 18:04
  • 2
    You misunderstood aschipfl's answer to an unrelated question. "The entire command line/block has already been parsed" makes no sense as an explanation here, The interpreter would have to partially parse the line and search the path to decide if the first command is a bat without call, and if so, somehow buffer the rest of the line for execution after the batch file returns—and all that effort would accomplish nothing but making batch files behave incorrectly. aschipfl was simply saying that the interpreter always reads whole lines at a time (no matter what the first part of the line is).
    – benrg
    Commented May 19 at 21:33
  • 1
    I don't understand how parsing can be the issue, unless the interpreter has some stunningly bad design (which I understand it does, but I didn't realise it was this bad). Most about any interpreter or compiler in popular use will parse the entirety of a A && B || C-like statement but only execute B or C depending on A's exit status. The actual reason seems to be what you explain later on: call needs to be used if the exit status has to be available for &&.
    – muru
    Commented May 21 at 1:28
1

There are a lot of strange things when it comes to calling Windows batch files.

Fix for your problem:

cmd /C error_check.bat && echo Second Command!
1
  • 3
    This does not explain why it didn't work. See my answer.
    – DavidPostill
    Commented May 19 at 8:42
0

Why is this the case?

Having thought about this, this behavior logically makes sense. Since the batch file will execute multiple commands ignoring failures, all that command chaining would achieve is operating on the result of the last command executed by the batch script.

Going forward I will be thinking of batch scripts as batch jobs that tolerate failures, rather than traditional sequential scripts that break on failure. With that mindset, it makes sense to not operate on the %errorlevel% returned by the batch script because, generally, that will be the %errorlevel% of the last command.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .