315

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.

Nakilon
  • 32,203
  • 13
  • 95
  • 132
Josh Kodroff
  • 25,181
  • 26
  • 90
  • 147
  • 2
    What command shell will be running your script? DOS/Win9x's command.com or Win2k+'s cmd.exe? Since that makes a world of difference, could you please clarify that in an edit of your question? – Mihai Limbășan Apr 09 '09 at 14:58

9 Answers9

324

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.

Community
  • 1
  • 1
system PAUSE
  • 33,478
  • 18
  • 60
  • 59
  • 19
    Is there a way to state it once for the entire file? "On error goto" or something similar? – Josh Kodroff Apr 09 '09 at 15:29
  • 3
    +1 for the negative errorlevel check. Had a script silently fail because of a negative result. – devstuff Sep 16 '10 at 04:37
  • 1
    Careful: the enabledelayedexpansion is CRITICAL and also required for an if/else or any other block – MarcH Apr 04 '13 at 23:05
  • `InstallUtil` returns -1 on failure. So yeah. – harpo Aug 31 '16 at 20:54
  • 1
    @system-PAUSE is there any difference between the first two 'if' shown? – simpleuser Sep 20 '16 at 22:09
  • 1
    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](https://technet.microsoft.com/en-us/library/bb490982.aspx) 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?](http://stackoverflow.com/questions/37515901/) – Mofi Jan 13 '17 at 16:56
  • Any idea how to get this to a single line? I tried using & to chain commands but the %errorlevel% doesn't contain what I'm looking for till after execution is done. – BradLaney Mar 22 '17 at 07:01
  • I don't really know how to interpret this answer. Does it mean that I just add the line `if %errorlevel% neq 0 exit /b %errorlevel%` at the end of the batch file? – Fabian Röling Nov 03 '17 at 18:38
  • @BradLaney, here is an alt. explanation. When the command line processes your lines it replaces all the %variables% with their values BEFORE the line is processed. So when you do something like: `ICauseProblems.bat && if %errorlevel% NEQ 0 echo Failed...` the %errorlevel% is filled in with whatever value it was BEFORE the 'ICauseProblems.bat' has even ran. When you turn on delayed expansion you can now ALSO do !variable! instead, and they're processing is delayed until their actual use. – Brent Rittenhouse Feb 05 '19 at 21:35
  • @FabianRöling [cont. ^--] So, you both need to use 'setlocal EnableDelayedExpansion' and then use !ErrorLevel! or whatever variable you want instead of %ErrorLevel%. If you are ever in a one-off situation you can do "cmd /v:on" to enable it outside of batch files. In fact, I modified my command prompt shortcut to be "cmd.exe /v:on & echo Delayed Expansion: Enabled" so it's always on. Just don't assume anyone else has this because it's probably so uncommon it's effectively just me =(. But.. if you wanna be a cool guy who almost ends his post in a sad face like me go ahead! =̶)̶ =(. Nice. – Brent Rittenhouse Feb 05 '19 at 21:39
  • I lied and now I can't edit what I said =/ To make that cmd.exe launch like I said you need to do: `cmd.exe /v:on /k "echo Delayed Expansion: Enabled... & echo.` Whatever you do just don't do what I ACTUALLY found out I did which is: My cmd.exe launches as this monstrocity: `cmd.exe /v:on /k "set "[unlikely]=1" & (echo. | set /p=Delayed Expansion: ) & if "![unlikely]!"=="1" (echo Enabled^!) else (echo Disabled... =/)`. I mean, it works... That's something. – Brent Rittenhouse Feb 05 '19 at 21:50
  • 1
    Take note that for some commands (e.g. Robocopy) there are some exitcode > 0 that is not really an error https://ss64.com/nt/robocopy-exit.html – Aditya Santoso Nov 03 '20 at 09:30
268

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.

Community
  • 1
  • 1
Fowl
  • 4,552
  • 2
  • 22
  • 39
  • 4
    It's a very common idiom in most shell scripting languages, and it reads well: "Do this, or this if it fails.." – Fowl Nov 30 '12 at 05:22
  • 4
    I use a SET to keep manually track of the line number: `command || (SET ErrorLine=102 && goto :error)` – SandRock Jul 08 '13 at 14:10
  • 1
    @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 Aug 12 '13 at 22:05
  • 3
    @jpmc26 yes it does, prove it it to yourself - `cmd /k exit -1 && echo success || echo fail` - prints fail. – Fowl Aug 12 '13 at 22:56
  • 2
    You can even avoid the labels with something like `command || exit /b %errorlevel%` – Johannes Brodwall Jul 26 '16 at 14:16
  • this is also commonly used in [Debian packages](http://unix.stackexchange.com/q/325705/44425) – phuclv Nov 27 '16 at 14:11
106

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
Benoit Blanchon
  • 10,737
  • 3
  • 56
  • 68
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
    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](https://msdn.microsoft.com/en-us/library/system.environment.exitcode.aspx). 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](http://stackoverflow.com/a/39040033/3074564) for a negative MS example on user expectations. – Mofi Jan 13 '17 at 17:15
23

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.

Erik Aronesty
  • 8,927
  • 4
  • 46
  • 29
13

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.

Xarn
  • 3,021
  • 20
  • 40
  • 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)`. – Mike Clark Apr 08 '21 at 09:07
1

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)
Amr Ali
  • 1,315
  • 13
  • 10
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

E.Seven
  • 11
  • 2
  • 1
    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`. – Stephan Sep 05 '17 at 18:10
-2
@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
PoweredByOrange
  • 6,525
  • 9
  • 39
  • 77
Demican
  • 11