The usual advice in batch / command scripting is to use exit /b to exit while setting ERRORLEVEL to indicate an error. However this doesn't play well with CMD's || and && operators. If I run these at the CMD command line:
C:\>echo @exit /b 1 > foo.cmd
C:\>foo.cmd && echo success || echo fail
success
(Expected output is fail).
According to ss64.com this is because "exit code" and ERRORLEVEL aren't always the same, and that exit can return a success (0) exit code when exiting with a non-0 ERRORLEVEL, and exit code is what ||/&&` are paying attention to.
Interestingly, adding call makes it work as I'd expect:
C:\>call foo.cmd && echo success || echo fail
fail
But requiring every use of ||/&& to also use call puts the burden on the consumer, rather than the implementation, of the script.
One example of when this might be useful is if I have a build.cmd script and a test.cmd script, I might want to run build && test.
https://ss64.com/nt/syntax-conditional.html and https://ss64.com/nt/exit.html don't mention this behavior, even though that site is usually very thorough about batch weirdness.
Why is CMD like this? What options exist to avoid this problem?
When you run a child script from inside of a CMD environment, flow control stays with the child script unless you use
callto return to the parent environment. This is more obvious if instead of using a one-liner, you had usedwhich doesn't return anything at all (since flow ended with the child script).
However, since you have everything on one line like that, the interpreter just goes "hang on; I have a block of other commands to run" and moves on to the next command (see How does the Windows Command Interpreter (CMD.EXE) parse scripts? for much more detail).
As you discovered, using
callto call child scripts makes the parent environment behave correctly, sincecalls are executed before regular commands.