Command script exit code not seen by same line && or ||? - windows

Consider a command script named t.cmd that consists solely of these 2 lines:
#exit /b 123
#echo If you see this, THEN EXIT FAILED..
So, the script merely sets the exit code of the script's execution process to 123, but does not kill cmd.exe. The final echo confirms that exit actually causes an immediate return (its output should not appear).
Now execute this script, and then print out the %errorlevel%:
>t.cmd
>echo %errorlevel%
123
So far so good: everything behaves exactly as expected.
But now execute the above all on one line, using && for conditional execution:
>t.cmd && echo %errorlevel%
123
I do NOT expect this: if t.cmd really returns with a non-0 exit code, then it should stop everything after that && (i.e. the echo) from executing. The fact that we see it print means that it DID execute. What the heck is going on?
And if execute the above all on one line, using || for conditional execution:
>t.cmd || echo %errorlevel%
>
This behavior is also the opposite of what I expect (altho it is consistent with the && behavior above).
Note that this weird behavior is ONLY true for bat files, but not for "raw commands".
Proof: consider the following command line interactions where instead of calling t.cmd, I try to execute the bogus command abcdef:
>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 bogus command.
So why do cmd files behave differently?
A possibly related bug in cmd.exe has been observed in File redirection in Windows and %errorlevel%
Also, I am aware that ERRORLEVEL is not %ERRORLEVEL%
By the way, the code above was all executed on a Win 7 Pro 64 bit box. I have no idea how other versions of Windows behave.

With t.bat slightly modified as follows:
#exit /b 123%~1
#echo If you see this, THEN EXIT FAILED..
Think out next 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
(%~1 etc. special page) Command Line arguments (Parameters)
(special page) EnableDelayedExpansion
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 next code snippet 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 'abcd' not recognized error should be 9009 whilst
|| keeps errorlevel 2 unchanged for FIND: Invalid switch error.
|| branch evaluates in call t.cmd 333 whilst
&& branch evaluates in t.cmd 222.
On (call ) see DBenham's answer:
If you want to force the errorlevel to 0, then you can use this
totally non-intuitive, but very effective syntax: (call ). The space
after call is critical.
If you want to set the errorlevel to 1, you can use (call). It
is critical that there not be any space after call.

%errorlevel% is expanded when line is read. So it is 123 at the time the line was read so comes from the previous command not t.exe. && is executed only if the current errorlevel (from t command) is 0.
See setlocal /? and set /? for more info.

#JosefZ's response provides excellent coverage of the disparity between ERRORLEVEL and exit code with respect to command scripts.
Unfortunately, as he pointed out, the && and || operators will only work if you invoke your command script with the call command. In most cases, you'd prefer that users can just run your command script, without having to remember to prefix it with a call each time.
Typically, I want my scripts to set both the ERRORLEVEL and the exit code to indicate failure (in order to have my scripts behave the same as regular executables). I used to use exit /b <nonzero> to try to do this, but had the problem indicated 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 exit /b <nonzero> command is, ironically, 0 (since it successfully exited). It does set the ERRORLEVEL, but not the exit code. Thus, that command won't work. The solution to all this is to (1) use the cmd /c exit <nonzero> command, and (2) to have it execute as the last command in the script. Since the cmd command returns back to the script, where the next command is then executed, the only way to get it to be the last line executed is to have it be the last line of your script.
Thus, here's a solution to get everything to behave as the OP requested:
#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

Related

Clear or reset ERRORLEVEL in a Windows batch script [duplicate]

I have a post-build event that runs some commands for a c# project. The last command would sometimes cause the ERRORLEVEL value not equals to zero and then the build fails.
I want to append an extra line of command to always set the ERRORLEVEL value to zero. What is the most convenient way to do that?
if you use exit /b 0 you can return an errorlevel 0 from within a child batch script without also exiting the parent.
Seems to do the trick:
ver > nul
Not everything works, and it is not clear why. For example, the following do not:
echo. > nul
cls > nul
In a pre- or post-build event, if the return code of an executable is greater than zero, and the call to the executable is not the last line of the pre- or post-build event, a quick way to mute it and avoid triggering a check for a non-zero errorlevel is to follow the failing line with a line that explicitly returns zero:
cmd /c "exit /b 0"
This is essentially a generic combination of the previously-mentioned solutions that will work with more than just the last line of a pre- or post-build event.
I personally use this:
cd .
Works even in unix shell.
But, this one might be a bit faster:
type nul>nul
Because Process Monitor shows QueryDirectory calls on cd .
PS:
cd . has another nice side effect in the unix shell. It does restore recreated working directory in the terminal if it has been opened before the erase.
Update:
And that is a bit more faster:
call;
Any windows command to find if a file is in use or not ? :https://www.dostips.com/forum/viewtopic.php?t=5542
I found that "exit 0" looks like a good way to deal with this problem.
Usage Example:
NET STOP UnderDevService /Y
exit 0
if the UnderDevService service is not started.
I use VERIFY or VERIFY > nul
If this is a snippet like "Post-build Event" etc., then you'll be fine appending:
(...) || ver > nul
at the end of the last command.
Alternatively
cmd /c "exit /b 0"
is very clean and non-idiomatic -- a reader who knows Windows shell will know what's going on, and what was your intent.
However, if you're in a batch script, you may want to use subrotines, which are a lightweight equivalent of the "child batch script" from akf's answer.
Have a subroutine:
:reset_error
exit /b 0
and then just
call :reset_error
wherever you need it.
Here's a complete example:
#echo off
rem *** main ***
call :raise_error
echo After :raise_error ERRORLEVEL = %ERRORLEVEL%
call :empty
echo After :empty ERRORLEVEL = %ERRORLEVEL%
call :reset_error
echo After :reset_error ERRORLEVEL = %ERRORLEVEL%
:: this is needed at the end of the main body of the script
goto:eof
rem *** subroutines ***
:empty
goto:eof
:raise_error
exit /b 1
:reset_error
exit /b 0
Which outputs:
After :raise_error ERRORLEVEL = 1
After :empty ERRORLEVEL = 1
After :reset_error ERRORLEVEL = 0
As you see - just calling and returning via goto:eof is not enough.
The following works in modern Windows (NT-based) systems that feature cmd.exe:
rem /* This clears `ErrorLevel`; the SPACE can actually be replaced by an
rem arbitrary sequence of SPACE, TAB, `,`, `;`, `=`, NBSP, VTAB, FF: */
(call )
The SPACE (or more precisely, an arbitrary sequence of one or more standard token separators, which are SPACE (code 0x20), TAB (code 0x09), ,, ;, =, NBSP (code 0xFF), VTAB (code 0x0B) and FF (code 0x0C)) is mandatory; if you omit it the ErrorLevel becomes set instead:
rem // This sets `ErrorLevel` to `1`:
(call)
There is a nice thread on DosTips.com where this technique came up.
Here is an alternative method, but which accesses the file system and might therefore be a bit slower:
dir > nul
rem /* Perhaps this is a little faster as a specific file is given rather
rem than just the current directory (`.` implicitly) like above: */
dir /B "%ComSpec%" > nul
Here are some other ways to reset the ErrorLevel state, which even work in MS-DOS (at least for version 6.22):
more < nul > nul
rem // The `> nul` part can be omitted in Windows but is needed in MS-DOS to avoid a line-break to be returned:
sort < nul > nul
The following methods work in MS-DOS only:
command /? > nul
fc nul nul > nul
keyb > nul
For the sake of completeness, this sets the ErrorLevel state to 1, valid for both Windows and MS-DOS:
< nul find ""
After reviewing all of the other answers, I decided to find which way was the most efficient for resetting the ERRORLEVEL. I made a quick script that recorded the time to execute each of these:
"cmd /c "exit /b 0"", "cd .", "ver", "type nul", and "VERIFY"
here is the output:
cmd /v:on /c set ^"q=^"^" & timeit.cmd "cmd /c ^!q^!exit /b 0^!q^!" "cd ." "ver" "type nul" "VERIFY"
cmd /c "exit /b 0" took 0:0:0.02 (0.02s total)
cd . took 0:0:0.00 (0.00s total)
Microsoft Windows [Version 10.0.18362.836]
ver took 0:0:0.00 (0.00s total)
type nul took 0:0:0.00 (0.00s total)
VERIFY is off.
VERIFY took 0:0:0.00 (0.00s total)
This took 0:0:0.06 (0.06s total)
after reviewing with Measure-Command {command} in Powershell, I found that it only really accepted cd . and cmd /c "exit /b 0" --am I doing something wrong?
I'd recommend either cd . or type nul since neither have a footprint on the output of the console, nor are they slow in any measure.
yeah I'm pretty bored
Add >nul after each command that's likely to fail - this seems to prevent the build from failing.
You can still check the result of the command by examining %errorlevel%.
For example:
findstr "foo" c:\temp.txt>nul & if %errorlevel% EQU 0 (echo found it) else (echo didn't find it)
I'm using this:
ping localhost -n 1 >null
I always just used;
set ERRORLEVEL=0
I've been using it for donkey's years.

Windows command line: why environment variable is not available after &

Have a look at the following commands: why is the value of a not available immediately after the &?
C:\>set a=
C:\>set a=3&echo %a%
%a%
C:\>echo %a%
3
C:\>set a=3&echo %a%
3
But when I do
C:\>set a=
C:\>set a=3&set
a=3 is included in the listed variables!
I need this for a trick I learned here, to get the exit code of a command even output is piped:
Windows command interpreter: how to obtain exit code of first piped command
but I have to use it in a make script, that's why everything must be in one line!
This is what I am trying to do:
target:
($(command) & call echo %%^^errorlevel%% ^>$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & exit /B %errorlevel_make%
But errorlevel_make is always empty (the file with the exit code exists and contains the correct exit code).
Is this a bug in cmd? Any ideas what I can do?
The reason for the observed behaviour is how the command line is processed.
In first case set a=3&echo %a%, when the line is interpreted BEFORE EXECUTING IT, all variables are replaced with their values. a has no value until line executes, so it can not be substituted in echo
In second case, set a=3&set, there is no variable substitution before execution. So, when set is executed, a has the value asigned.
It's much easier to create a seperate batch file to solve this, as it isn't obvious even for experts.
But in your case this should work
target:
($(command) & call echo %^^^^errorlevel% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & call exit /B %^errorlevel_make%
A seperate batch could look like
extBatch.bat
#echo off
("%~1" & call echo %%^^errorlevel%% > "%~2") 2>&1 | tee "%~3" & set /p errorlevel_make=<"%~2"
exit /B %errorlevel_make%
Then you could start the batch from your make file
target:
extBatch.bat $(command) $(exitcodefile) $(logfile)
Inspired by the nice answer of jeb, I modified the code a little to be able to grab errorlevel for each side of the pipe, separately. For example you have the following pipe in your batch file:
CD non_exisitng 2>&1 | FIND /V ""
ECHO Errorlevel #right: %errorlevel%
The errorlevel above refers to the right-hand side of the pipe only, and it indicates if the FIND command succeeded of not.
However, to be able to get the errorlevel of both sides, we can change the above code to something like this:
(CD non_exisitng & CALL ECHO %%^^errorlevel%% >err) 2>&1 | FIND /V ""
ECHO Errorlevel #right: %errorlevel%
SET /P err=<err
ECHO Errorlevel #left: %err%
output:
The system cannot find the path specified.
errorlevel #right: 0
errorlevel #left: 1
#OP:
I realize this comes a bit late, but might be helpful for others. In addition to jeb's method of an external batch file, the original problem can also be solved as one-liner:
target:
($(command) & call echo %%^^errorlevel%% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & cmd /c call exit /B %%errorlevel_make%%

CMD Script Errorlevel Misbehavior with IF statement

Expected outcome of the following script: PERMPING if user presses P, or PINGLOOP if user presses T. However, no matter what the user presses, the script echos both. Any idea what gives?
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF ERRORLEVEL 1 ECHO PERMPING
IF ERRORLEVEL 2 ECHO PINGLOOP
I suspect you are not acurately reporting the results of your code. The code you have posted should print both if P is pressed, and only print PINGLOOP if T is pressed. The behavior is due to how the IF ERRORLEVEL statement works, as is explained by the help. To get help on any command, simply type HELP commandName or commandName /? from a command prompt. In your case, you could use IF /? to see the following
Performs conditional processing in batch programs.
IF [NOT] ERRORLEVEL number command
...
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
...
You have 2 choices to make your code work:
Test the conditions in decreasing numerical order and use the ELSE clause
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF ERRORLEVEL 2 (
ECHO PINGLOOP
) ELSE IF ERRORLEVEL 1 (
ECHO PERMPING
)
or use IF %ERRORLEVEL%==N
CHOICE /C:PT /N /M "Ping permanently (P) or temporarily (T) (%pingTimes% times)?"
echo %ERRORLEVEL%
IF %ERRORLEVEL% == 1 ECHO PERMPING
IF %ERRORLEVEL% == 2 ECHO PINGLOOP
To expand on the answer above if command extensions are turned on you can also use:
if %errorlevel% equ 1 echo PERMPING
if %errorlevel% equ 2 echo PINGLOOP

How to conditionally take action if FINDSTR fails to find a string

I have a batch file as follows;
CD C:\MyFolder
findstr /c:"stringToCheck" fileToCheck.bat
IF NOT XCOPY "C:\OtherFolder\fileToCheck.bat" "C:\MyFolder" /s /y
I am getting an error ("C:\OtherFolder\fileToCheck.bat" was unexpected at this time.) when trying to execute this.
Please let me know what I am doing wrong.
I presume you want to copy C:\OtherFolder\fileToCheck.bat to C:\MyFolder if the existing file in C:\MyFolder is either missing entirely, or if it is missing "stringToCheck".
FINDSTR sets ERRORLEVEL to 0 if the string is found, to 1 if it is not. It also sets errorlevel to 1 if the file is missing. It also prints out each line that matches. Since you are trying to use it as a condition, I presume you don't need or want to see any of the output. The 1st thing I would suggest is to redirect both the normal and error output to nul using >nul 2>&1.
Solution 1 (mostly the same as previous answers)
You can use IF ERRORRLEVEL N to check if the errorlevel is >= N. Or you can use IF NOT ERRORLEVEL N to check if errorlevel is < N. In your case you want the former.
findstr /c:"stringToCheck" "c:\MyFolder\fileToCheck.bat" >nul 2>&1
if errorlevel 1 xcopy "C:\OtherFolder\fileToCheck.bat" "c:\MyFolder"
Solution 2
You can test for a specific value of errorlevel by using %ERRORLEVEL%. You can probably check if the value is equal to 1, but it might be safer to check if the value is not equal to 0, since it is only set to 0 if the file exists and it contains the string.
findstr /c:"stringToCheck" "c:\MyFolder\fileToCheck.bat" >nul 2>&1
if not %errorlevel% == 0 xcopy "C:\OtherFolder\fileToCheck.bat" "c:\MyFolder"
or
findstr /c:"stringToCheck" "c:\MyFolder\fileToCheck.bat" >nul 2>&1
if %errorlevel% neq 0 xcopy "C:\OtherFolder\fileToCheck.bat" "c:\MyFolder"
Solution 3
There is a very compact syntax to conditionally execute a command based on the success or failure of the previous command: cmd1 && cmd2 || cmd3 which means execute cmd2 if cmd1 was successful (errorlevel=0), else execute cmd3 if cmd1 failed (errorlevel<>0). You can use && alone, or || alone. All the commands need to be on the same line. If you need to conditionally execute multiple commands you can use multiple lines by adding parentheses
cmd1 && (
cmd2
cmd3
) || (
cmd4
cmd5
)
So for your case, all you need is
findstr /c:"stringToCheck" "c:\MyFolder\fileToCheck.bat" >nul 2>&1 || xcopy "C:\OtherFolder\fileToCheck.bat" "c:\MyFolder"
But beware - the || will respond to the return code of the last command executed. In my earlier pseudo code the || will obviously fire if cmd1 fails, but it will also fire if cmd1 succeeds but then cmd3 fails.
So if your success block ends with a command that may fail, then you should append a harmless command that is guaranteed to succeed. I like to use (CALL ), which is harmless, and always succeeds. It also is handy that it sets the ERRORLEVEL to 0. There is a corollary (CALL) that always fails and sets ERRORLEVEL to 1.
You are not evaluating a condition for the IF. I am guessing you want to not copy if you find stringToCheck in fileToCheck. You need to do something like (code untested but you get the idea):
CD C:\MyFolder
findstr /c:"stringToCheck" fileToCheck.bat
IF NOT ERRORLEVEL 0 XCOPY "C:\OtherFolder\fileToCheck.bat" "C:\MyFolder" /s /y
EDIT by dbenham
The above test is WRONG, it always evaluates to FALSE.
The correct test is IF ERRORLEVEL 1 XCOPY ...
Update: I can't test the code, but I am not sure what return value findstr actually returns if it doesn't find anything. You might have to do something like:
CD C:\MyFolder
findstr /c:"stringToCheck" fileToCheck.bat > tempfindoutput.txt
set /p FINDOUTPUT= < tempfindoutput.txt
IF "%FINDOUTPUT%"=="" XCOPY "C:\OtherFolder\fileToCheck.bat" "C:\MyFolder" /s /y
del tempfindoutput.txt
In DOS/Windows Batch most commands return an exitCode, called "errorlevel", that is a value that customarily is equal to zero if the command ends correctly, or a number greater than zero if ends because an error, with greater numbers for greater errors (hence the name).
There are a couple methods to check that value, but the original one is:
IF ERRORLEVEL value command
Previous IF test if the errorlevel returned by the previous command was GREATER THAN OR EQUAL the given value and, if this is true, execute the command. For example:
verify bad-param
if errorlevel 1 echo Errorlevel is greater than or equal 1
echo The value of errorlevel is: %ERRORLEVEL%
Findstr command return 0 if the string was found and 1 if not:
CD C:\MyFolder
findstr /c:"stringToCheck" fileToCheck.bat
IF ERRORLEVEL 1 XCOPY "C:\OtherFolder\fileToCheck.bat" "C:\MyFolder" /s /y
Previous code will copy the file if the string was NOT found in the file.
CD C:\MyFolder
findstr /c:"stringToCheck" fileToCheck.bat
IF NOT ERRORLEVEL 1 XCOPY "C:\OtherFolder\fileToCheck.bat" "C:\MyFolder" /s /y
Previous code copy the file if the string was found. Try this:
findstr "string" file
if errorlevel 1 (
echo String NOT found...
) else (
echo String found
)
I tried to get this working using FINDSTR, but for some reason my "debugging" command always output an error level of 0:
ECHO %ERRORLEVEL%
My workaround is to use Grep from Cygwin, which outputs the right errorlevel (it will give an errorlevel greater than 0) if a string is not found:
dir c:\*.tib >out 2>>&1
grep "1 File(s)" out
IF %ERRORLEVEL% NEQ 0 "Run other commands" ELSE "Run Errorlevel 0 commands"
Cygwin's grep will also output errorlevel 2 if the file is not found. Here's the hash from my version:
C:\temp\temp>grep --version
grep (GNU grep) 2.4.2
C:\cygwin64\bin>md5sum grep.exe
c0a50e9c731955628ab66235d10cea23 *grep.exe
C:\cygwin64\bin>sha1sum grep.exe
ff43a335bbec71cfe99ce8d5cb4e7c1ecdb3db5c *grep.exe

What is the easiest way to reset ERRORLEVEL to zero?

I have a post-build event that runs some commands for a c# project. The last command would sometimes cause the ERRORLEVEL value not equals to zero and then the build fails.
I want to append an extra line of command to always set the ERRORLEVEL value to zero. What is the most convenient way to do that?
if you use exit /b 0 you can return an errorlevel 0 from within a child batch script without also exiting the parent.
Seems to do the trick:
ver > nul
Not everything works, and it is not clear why. For example, the following do not:
echo. > nul
cls > nul
In a pre- or post-build event, if the return code of an executable is greater than zero, and the call to the executable is not the last line of the pre- or post-build event, a quick way to mute it and avoid triggering a check for a non-zero errorlevel is to follow the failing line with a line that explicitly returns zero:
cmd /c "exit /b 0"
This is essentially a generic combination of the previously-mentioned solutions that will work with more than just the last line of a pre- or post-build event.
I personally use this:
cd .
Works even in unix shell.
But, this one might be a bit faster:
type nul>nul
Because Process Monitor shows QueryDirectory calls on cd .
PS:
cd . has another nice side effect in the unix shell. It does restore recreated working directory in the terminal if it has been opened before the erase.
Update:
And that is a bit more faster:
call;
Any windows command to find if a file is in use or not ? :https://www.dostips.com/forum/viewtopic.php?t=5542
I found that "exit 0" looks like a good way to deal with this problem.
Usage Example:
NET STOP UnderDevService /Y
exit 0
if the UnderDevService service is not started.
I use VERIFY or VERIFY > nul
If this is a snippet like "Post-build Event" etc., then you'll be fine appending:
(...) || ver > nul
at the end of the last command.
Alternatively
cmd /c "exit /b 0"
is very clean and non-idiomatic -- a reader who knows Windows shell will know what's going on, and what was your intent.
However, if you're in a batch script, you may want to use subrotines, which are a lightweight equivalent of the "child batch script" from akf's answer.
Have a subroutine:
:reset_error
exit /b 0
and then just
call :reset_error
wherever you need it.
Here's a complete example:
#echo off
rem *** main ***
call :raise_error
echo After :raise_error ERRORLEVEL = %ERRORLEVEL%
call :empty
echo After :empty ERRORLEVEL = %ERRORLEVEL%
call :reset_error
echo After :reset_error ERRORLEVEL = %ERRORLEVEL%
:: this is needed at the end of the main body of the script
goto:eof
rem *** subroutines ***
:empty
goto:eof
:raise_error
exit /b 1
:reset_error
exit /b 0
Which outputs:
After :raise_error ERRORLEVEL = 1
After :empty ERRORLEVEL = 1
After :reset_error ERRORLEVEL = 0
As you see - just calling and returning via goto:eof is not enough.
The following works in modern Windows (NT-based) systems that feature cmd.exe:
rem /* This clears `ErrorLevel`; the SPACE can actually be replaced by an
rem arbitrary sequence of SPACE, TAB, `,`, `;`, `=`, NBSP, VTAB, FF: */
(call )
The SPACE (or more precisely, an arbitrary sequence of one or more standard token separators, which are SPACE (code 0x20), TAB (code 0x09), ,, ;, =, NBSP (code 0xFF), VTAB (code 0x0B) and FF (code 0x0C)) is mandatory; if you omit it the ErrorLevel becomes set instead:
rem // This sets `ErrorLevel` to `1`:
(call)
There is a nice thread on DosTips.com where this technique came up.
Here is an alternative method, but which accesses the file system and might therefore be a bit slower:
dir > nul
rem /* Perhaps this is a little faster as a specific file is given rather
rem than just the current directory (`.` implicitly) like above: */
dir /B "%ComSpec%" > nul
Here are some other ways to reset the ErrorLevel state, which even work in MS-DOS (at least for version 6.22):
more < nul > nul
rem // The `> nul` part can be omitted in Windows but is needed in MS-DOS to avoid a line-break to be returned:
sort < nul > nul
The following methods work in MS-DOS only:
command /? > nul
fc nul nul > nul
keyb > nul
For the sake of completeness, this sets the ErrorLevel state to 1, valid for both Windows and MS-DOS:
< nul find ""
After reviewing all of the other answers, I decided to find which way was the most efficient for resetting the ERRORLEVEL. I made a quick script that recorded the time to execute each of these:
"cmd /c "exit /b 0"", "cd .", "ver", "type nul", and "VERIFY"
here is the output:
cmd /v:on /c set ^"q=^"^" & timeit.cmd "cmd /c ^!q^!exit /b 0^!q^!" "cd ." "ver" "type nul" "VERIFY"
cmd /c "exit /b 0" took 0:0:0.02 (0.02s total)
cd . took 0:0:0.00 (0.00s total)
Microsoft Windows [Version 10.0.18362.836]
ver took 0:0:0.00 (0.00s total)
type nul took 0:0:0.00 (0.00s total)
VERIFY is off.
VERIFY took 0:0:0.00 (0.00s total)
This took 0:0:0.06 (0.06s total)
after reviewing with Measure-Command {command} in Powershell, I found that it only really accepted cd . and cmd /c "exit /b 0" --am I doing something wrong?
I'd recommend either cd . or type nul since neither have a footprint on the output of the console, nor are they slow in any measure.
yeah I'm pretty bored
Add >nul after each command that's likely to fail - this seems to prevent the build from failing.
You can still check the result of the command by examining %errorlevel%.
For example:
findstr "foo" c:\temp.txt>nul & if %errorlevel% EQU 0 (echo found it) else (echo didn't find it)
I'm using this:
ping localhost -n 1 >null
I always just used;
set ERRORLEVEL=0
I've been using it for donkey's years.

Resources