389

I have a batch file that's calling the same executable over and over with different parameters. How do I make it terminate immediately if one of the calls returns an error code of any level?

Basically, I want the equivalent of MSBuild's ContinueOnError=false.

0

9 Answers 9

384

Check the errorlevel in an if statement, and then exit /b (exit the batch file only, not the entire cmd.exe process) for values other than 0.

same-executable-over-and-over.exe /with different "parameters" if %errorlevel% neq 0 exit /b %errorlevel% 

If you want the value of the errorlevel to propagate outside of your batch file

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

but if this is inside a for it gets a bit tricky. You'll need something more like:

setlocal enabledelayedexpansion for %%f in (C:\Windows\*) do ( same-executable-over-and-over.exe /with different "parameters" if !errorlevel! neq 0 exit /b !errorlevel! ) 

Edit: You have to check the error after each command. There's no global "on error goto" type of construct in cmd.exe/command.com batch. I've also updated my code per CodeMonkey, although I've never encountered a negative errorlevel in any of my batch-hacking on XP or Vista.

Sign up to request clarification or add additional context in comments.

12 Comments

Is there a way to state it once for the entire file? "On error goto" or something similar?
+1 for the negative errorlevel check. Had a script silently fail because of a negative result.
Careful: the enabledelayedexpansion is CRITICAL and also required for an if/else or any other block
Delayed expansion enabled/disabled or command extensions (required for neq) enabled/disabled does not matter on using if not errorlevel 1 exit /B as explained by Microsoft in support article Using command redirection operators and in help output on running if /? in a cmd window. The current errorlevel (exit code) is kept on exiting processing of batch file with exit /B. Note: exit with parameter /B requires enabled command extensions, see Where does GOTO :EOF return to?
Take note that for some commands (e.g. Robocopy) there are some exitcode > 0 that is not really an error ss64.com/nt/robocopy-exit.html
|
299

Add || goto :label to each line, and then define a :label.

For example, create this .cmd file:

@echo off echo Starting very complicated batch file... ping -invalid-arg || goto :error echo OH noes, this shouldn't have succeeded. goto :EOF :error echo Failed with error #%errorlevel%. exit /b %errorlevel% 

See also question about exiting batch file subroutine.

6 Comments

It's a very common idiom in most shell scripting languages, and it reads well: "Do this, or this if it fails.."
I use a SET to keep manually track of the line number: command || (SET ErrorLine=102 && goto :error)
@MarcelValdezOrozco Seems to me that this is what || was created for in the first place. Maybe not goto in particular, but "try, do this on error" as Fowl mentioned. My question is does this work for all non-zero exit codes? Positives only?
@jpmc26 yes it does, prove it it to yourself - cmd /k exit -1 && echo success || echo fail - prints fail.
You can even avoid the labels with something like command || exit /b %errorlevel%
|
142

The shortest:

command || exit /b 

If you need, you can set the exit code:

command || exit /b 666 

And you can also log:

command || echo ERROR && exit /b 

4 Comments

does exit /b by chance return the original failing exit code?
@FrankSchwieterman, yes, %ERRORLEVEL% is untouched when you call exit /b, so the error code is forwarded
Actually this snippet doesn't really work as expected! I used it first and upvoted, but then I realized it doesn't work! When my command is calling some Python script which exits (python -u fail.py, fail.py is sys.exit(1)), then using || exit /b always gives me zero! Maybe it's specific of Python error codes, I don't know, but straightforward handling with if %errorlevel% neq 0 afterwards works perfectly. So be aware!
@TheGodfather I can't reproduce your finding. I'm running Python 3.12.0 on Windows. With py -u fail.py || exit /b the %errorlevel% is set to 1, with py -u fail.py || exit /b 666 it's set to 666.
40

Here is a polyglot program for BASH and Windows CMD that runs a series of commands and quits out if any of them fail:

#!/bin/bash 2> nul :; set -o errexit :; function goto() { return $?; } command 1 || goto :error command 2 || goto :error command 3 || goto :error :; exit 0 exit /b 0 :error exit /b %errorlevel% 

I have used this type of thing in the past for a multiple platform continuous integration script.

Comments

24

One minor update, you should change the checks for "if errorlevel 1" to the following...

IF %ERRORLEVEL% NEQ 0 

This is because on XP you can get negative numbers as errors. 0 = no problems, anything else is a problem.

And keep in mind the way that DOS handles the "IF ERRORLEVEL" tests. It will return true if the number you are checking for is that number or higher so if you are looking for specific error numbers you need to start with 255 and work down.

1 Comment

None of the Windows standard internal and external commands exit ever with a negative value. Microsoft warns any program writer to exit with a negative value, for example on MSDN article about Environment.ExitCode Property. In fact it must be always find out which exit code is used by an application on success and which ones on the various errors, see HTML Help Workshop returns error after successfully compiled .chm file for a negative MS example on user expectations.
19

I prefer the OR form of command, as I find them the most readable (as opposed to having an if after each command). However, the naive way of doing this, command || exit /b %ERRORLEVEL% is wrong.

This is because batch expands variables when a line is first read, rather than when they are being used. This means that if the command in the line above fails, the batch file exits properly, but it exits with return code 0, because that is what the value of %ERRORLEVEL% was at the start of the line. Obviously, this is undesirable in our script, so we have to enable delayed expansion, like so:

SETLOCAL EnableDelayedExpansion command-1 || exit /b !ERRORLEVEL! command-2 || exit /b !ERRORLEVEL! command-3 || exit /b !ERRORLEVEL! command-4 || exit /b !ERRORLEVEL! 

This snippet will execute commands 1-4, and if any of them fails, it will exit with the same exit code as the failing command did.

3 Comments

Your code is correct, but I believe unnecessarily verbose for simple tasks: exit /b with no argument will preserve the errorlevel. Your example could be simplified to command-1 || exit /b -- shorter and no longer requires EnableDelayedExpansion. However, your code does demonstrate valid use of EnableDelayedExpansion, which someone might build upon, for example: command-1 || (echo Error=!errorlevel! && exit /b).
@MikeClark, I don't think that's correct. I just tested it and only using delayed expansion and error level failed the batch correctly.
@MikeClark Since you do a echo command before the exit command, I expect errorlevel will be reset by the echo command, i.e. always zero
2

We cannot always depend on ERRORLEVEL, because many times external programs or batch scripts do not return exit codes.

In that case we can use generic checks for failures like this:

IF EXIST %outfile% (DEL /F %outfile%) CALL some_script.bat -o %outfile% IF NOT EXIST %outfile% (ECHO ERROR & EXIT /b) 

And if the program outputs something to console, we can check it also.

some_program.exe 2>&1 | FIND "error message here" && (ECHO ERROR & EXIT /b) some_program.exe 2>&1 | FIND "Done processing." || (ECHO ERROR & EXIT /b) 

Comments

1

No matter how I tried, the errorlevel always stays 0 even when msbuild failed. So I built my workaround:

Build Project and save log to Build.log

SET Build_Opt=/flp:summary;logfile=Build.log;append=true msbuild "myproj.csproj" /t:rebuild /p:Configuration=release /fl %Build_Opt% 

search for "0 Error" string in build log, set the result to var

FOR /F "tokens=* USEBACKQ" %%F IN (`find /c /i "0 Error" Build.log`) DO ( SET var=%%F ) echo %var% 

get the last character, which indicates how many lines contains the search string

set result=%var:~-1% echo "%result%" 

if string not found, then error > 0, build failed

if "%result%"=="0" ( echo "build failed" ) 

That solution was inspired by Mechaflash's post at How to set commands output as a variable in a batch file

and https://ss64.com/nt/syntax-substring.html

1 Comment

works only for counts lss than ten. Better; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Note: find "0 Error" will also find 10 Errors.
-3
@echo off set startbuild=%TIME% C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe c:\link.xml /flp1:logfile=c:\link\errors.log;errorsonly /flp2:logfile=c:\link\warnings.log;warningsonly || goto :error copy c:\app_offline.htm "\\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm" del \\lawpccnweb01\d$\websites\OperationsLinkWeb\bin\ /Q echo Start Copy: %TIME% set copystart=%TIME% xcopy C:\link\_PublishedWebsites\OperationsLink \\lawpccnweb01\d$\websites\OperationsLinkWeb\ /s /y /d del \\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm echo Started Build: %startbuild% echo Started Copy: %copystart% echo Finished Copy: %TIME% c:\link\warnings.log :error c:\link\errors.log 

2 Comments

Please add more information to your answer. Just a block of code is not very helpful.
Besides not having added any comments, your snippet doesn't look like a good candidate for explaining a functionality: it seems to contain a lot of things that are completely irrelevant to the question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.