'Set errorlevel in Windows batch file
I am writing a batch script that will loop through each line of a text file, (each line containing a filename) check if the file exists and then runs the file and moves it.
Here is my batch script:
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (./ready/input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if not exist .\ready\%%i set errorlevel=2
echo return code is %errorlevel%
ECHO Run %%i if it exists
if errorlevel 0 call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 0 copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
ECHO Copy line of text to the new output.txt file if an error occured
if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
)
Here is the output:
I do not understand why the "if errorlevel" is not working as expected... if the file does not exist (as in this example where it does not exist) it should NOT try to run the file, it should NOT copy the file, and it should echo a 2 not a 0
Edit 1: I was reading another SO Post regarding "delayed environment variable expansion" I am not sure if this issue is related
Solution 1:[1]
@ECHO OFF
SETLOCAL
DEL output.txt 2>nul
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if exist .\ready\%%i (set "errorflag=") ELSE (set errorflag=2)
CALL echo return code is %%errorflag%%
ECHO Run %%i if it exists
if NOT DEFINED errorflag (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 1 (SET errorflag=3) ELSE (ECHO copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i)
)
ECHO Copy line of text to the new output.txt file if an error occured
if DEFINED errorflag >>output.txt ECHO %%i, %%j, %%k
)
GOTO :EOF
Here's a rewritten procedure.
Note: output.txt
is deleted at the start, else the >>
would append to any existing file. 2>nul
suppresses error messages if the delete fails (eg. file not exist)
Within a block statement (a parenthesised series of statements)
, the ENTIRE block is parsed and THEN executed. Any %var%
within the block will be replaced by that variable's value AT THE TIME THE BLOCK IS PARSED - before the block is executed.
Hence, IF (something) else (somethingelse)
will be executed using the values of %variables%
at the time the IF
is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion
and use !var!
in place of %var%
to access the chnaged value of var
or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%%
which displays the changed value of var
. CALL ECHO %%errorlevel%%
displays, but sadly then RESETS errorlevel.
IF DEFINED var
is true if var
is CURRENTLY defined.
ERRORLEVEL
is a special varable name. It is set by the system, but if set by the user, the user-assigned value overrides the system value.
IF ERRORLEVEL n
is TRUE if errorlevel
is n OR GREATER THAN n. IF ERRORLEVEL 0
is therefore always true.
The syntax SET "var=value"
(where value may be empty) is used to ensure that any stray spaces at the end of a line are NOT included in the value assigned.
The required commands are merely ECHO
ed for testing purposes. After you've verified that the commands are correct, change ECHO COPY
to COPY
to actually copy the files.
I used the following input.txt
:
seterr1.bat, J1, K1
seterr5.bat,J2,K2
seterr0.bat,J3 K3
seterr5.bat, J4, K4
notexist.bat, J5, K5
With existing files seterr*.bat
which contain
@ECHO OFF
EXIT /b 1
(where the 1
in the last line determines the errorlevel
returned)
and received the resultant output:
Check seterr1.bat exists, set error flag if it doesnt
return code is
Run seterr1.bat if it exists
Move seterr1.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr0.bat exists, set error flag if it doesnt
return code is
Run seterr0.bat if it exists
Move seterr0.bat to archive if no error occured
copy .\ready\seterr0.bat .\archive\__J3_K3_seterr0.bat
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check notexist.bat exists, set error flag if it doesnt
return code is 2
Run notexist.bat if it exists
Copy line of text to the new output.txt file if an error occured
Note that the COPY is merely ECHO
ed as I mentioned earlier.
and output.txt
seterr1.bat, J1, K1
seterr5.bat, J2, K2
seterr5.bat, J4, K4
notexist.bat, J5, K5
Solution 2:[2]
ERRORLEVEL
and %ERRORLEVEL%
are two different variables. That means your code with echo return code is %errorlevel%
and if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
is probably wrong.
ERRORLEVEL
is builtin and used to fetch the result of the last command. You can use it like:
IF ERRORLEVEL 1 ECHO error level is 1 or more
ERRORLEVEL
cannot be set, just like bash does not let you set ?= ...
%ERRORLEVEL%
is an environmental variable. If %ERRORLEVEL%
is set, then its used in your script when you use %ERRORLEVEL%
. If %ERRORLEVEL%
is not set AND if command extensions are enabled, then it falls back to ERRORLEVEL
. ERRORLEVEL
does not update %ERRORLEVEL%
.
Raymond Chen has a good blog entry on it: ERRORLEVEL is not %ERRORLEVEL%
. Some of the content in this answer was shamelessly lifted from it.
Solution 3:[3]
Use something like the following subroutine:
:return
ECHO @exit /b %1 >ret.cmd
CALL ret.cmd
GOTO :eof
Then use it like this:
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return !retcode!
ENDLOCAL
CALL :eof
So, the whole thing would looke something like:
test.cmd...
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :Attempt
IF !errorlevel! NEQ 0 (ECHO Attempt Failed) ELSE (ECHO Attempt succeeded!)
GOTO :eof
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return %retcode%
ENDLOCAL
CALL :eof
:return
ECHO @exit /b %1 >return.cmd
CALL ret.bat
GOTO :eof
somethingthatfails.cmd...
DIR some command that fails >nul 2>&1
somethingthatpasses.cmd...
DIR >nul 2>&1
The one side effect of this is a file laying around called ret.cmd. I usually use an :end subroutine that does cleanup and would delete it.
Solution 4:[4]
There is an easy way to set the %errorlevel%
with a trick I learned several years ago:
:: force errorlevel to 1
@(call)
echo %errorlevel%
:: force errorlevel to 0
@(call )
echo %errorlevel%
pause
The space after call
is necessary to set the %errorlevel%
to 0
.
Update: After researching this, I found a reference here.
Solution 5:[5]
This is designed to execute the %%i item only if it exists and follow through with checking for errors and move or log. if the %%i item doesn't exist then it will do nothing.
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, execute it if it does
if exist .\ready\%%i (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if not errorlevel 1 (
copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
) else (
ECHO Copy line of text to the new output.txt file if an error occurred
>>output.txt %%i, %%j, %%k
)
)
)
Solution 6:[6]
for me, simple use of cmd /c exit 2
worked to set the errorlevel
and use it locally in a batch file and even after it ended to ask for the errorlevel
outside:
set errorlevel=2
:
cmd /c exit %errorlevel%
:
if errorlevel 3 echo 3
if errorlevel 2 echo 2
if errorlevel 1 echo 1
if errorlevel 1 echo 0
Results
>test.bat
2
1
0
>if errorlevel 2 echo 2
2
Solution 7:[7]
For posterity, when specifically setting it to 0, I like
ver >nul
ver.exe always returns 0.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Magoo |
Solution 2 | anatolyg |
Solution 3 | |
Solution 4 | Toni |
Solution 5 | |
Solution 6 | pltc |
Solution 7 | Adam B |