Is ERRORLEVEL reliable? - batch-file

Since ERRORLEVEL is an environment variable isn't it possible that its value can be changed before I get a chance to check it in my batch file?

Environment variables belong to the current "process" so they can't be changed from outside. Provided you check the error level after the relevant command in your batch file, you should be checking the right value.
You can confirm this by opening up two command windows and entering into the first:
c:> set errorlevel=7
and then the second:
c:> set errorlevel=9
then go back to the first and:
c:> echo %errorlevel%
7
You should be very careful about setting the errorlevel environment variable by the way. That particular variable is a special one in that, when you haven't set it specifically, it will automatically serve up the return code from the previous program.
Setting it explicitly overrides this behaviour, and the only way to recover is to either use if errorlevel N(a) instead of the environment variable (it bypasses the environment variable), or use set errorlevel= to remove the override.
(a) The correct way to do this is mandated by fact that the errorlevel expression is true if the error level is greater than or equal to the value specified. Because of that, you should do it in reverse order, something like:
if errorlevel 3 goto :got3ormore
if errorlevel 2 goto :got2
if errorlevel 1 goto :got1
if errorlevel 0 goto :got0
goto :gotnegative

%errorlevel% (it is not really case sensitive) is a very special environment variable. It is not at all meant for setting by users or inside batch scripts.
Also, you may run into the problem that you execute the call set errorlevel=15 but the very fact that you successfully executed the set command, may have reset the %errorlevel% you're expecting to now evaluete 0.
Should you need it
The only "legal" way to evaluate the %errorlevel% environment variable is using repeated if errorlevel statements:
if errorlevel 0 do somecommand
if errorlevel 1 do somethingelse
...
if errorlevel 255 do lastalternative
everything else is dangerous. If you indeed need to use the current %errorlevel% a few lines later, then use something like
if errorlevel 0 do set myerrorlevel=0
and then readout %myerrorlevel%...

Related

ERRORLEVEL of a FINDSTR command always returning 0

I have to remove an entry in the hosts file on some devices. The FINDSTR command is working, but always returning errorlevel 0.
I know that by default, the %errorlevel% is always 0.
So if the previous command is not setting the error code it will always return 0.
I basically need to find a string in the hosts file, if it exists it should save a new hosts file without the line containing the string.
This is what I do:
set HOSTSFILE=%WINDIR%\system32\drivers\etc\hosts
set TEMP_HOSTS=%TEMP%\%RANDOM%__hosts
FINDSTR /V /I /C:"string to search for" "%HOSTSFILE%" > "%TEMP_HOSTS%"
IF %ERRORLEVEL% EQU 0 COPY /b/v/y "%TEMP_HOSTS%" "%HOSTSFILE%"
This basically works. However, the above code will always return errorlevel 0 and thus always copy over the hosts file, even if the string is not present in the file.
I know it doesn't really matter, but I'm curious as to why the errorlevel is not working here?
The errorlevel should be set by the previous command, which in this case is the FINDSTR.
I've also tried with the FIND command and with !errorlevel! instead of %errorlevel%.

Batch file to open a file based on the CPU usage

#Echo OFF
SET "Caminho=SomePath"
SET /A "UsoMaximo=95"
SET /A "Intervalo=3"
:LOOP
cls
For /F %%P in ('wmic cpu get loadpercentage ^| FINDSTR "[0-9]"') do (
IF %%P GTR %UsoMaximo% (
start %Caminho%
)
)
Ping -n %Intervalo% Localhost >NUL
GOTO :LOOP
I made this code based (almost cloned) on the code in this question: Batch file to restart a specific service based on the CPU of a process.
Basically, it will open a file located in "Caminho" when the CPU usage is higher than 95%.
The problem is i don't want it to open when the CPU usage is higher than 95%, i want it to open when the CPU usage is LOWER than 95%.
How can i do this? What i need to change?
Change GTR to LSS.
IF %%P GTR %UsoMaximo% (
From if /?:
Performs conditional processing in batch programs.
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
NOT Specifies that Windows should carry out
the command only if the condition is false.
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
string1==string2 Specifies a true condition if the specified text strings
match.
EXIST filename Specifies a true condition if the specified filename
exists.
command Specifies the command to carry out if the condition is
met. Command can be followed by ELSE command which
will execute the command after the ELSE keyword if the
specified condition is FALSE
The ELSE clause must occur on the same line as the command after the IF. For
example:
IF EXIST filename. (
del filename.
) ELSE (
echo filename. missing.
)
The following would NOT work because the del command needs to be terminated
by a newline:
IF EXIST filename. del filename. ELSE echo filename. missing
Nor would the following work, since the ELSE command must be on the same line
as the end of the IF command:
IF EXIST filename. del filename.
ELSE echo filename. missing
The following would work if you want it all on one line:
IF EXIST filename. (del filename.) ELSE echo filename. missing
If Command Extensions are enabled IF changes as follows:
IF [/I] string1 compare-op string2 command
IF CMDEXTVERSION number command
IF DEFINED variable command
where compare-op may be one of:
EQU - equal
NEQ - not equal
LSS - less than
LEQ - less than or equal
GTR - greater than
GEQ - greater than or equal
and the /I switch, if specified, says to do case insensitive string
compares. The /I switch can also be used on the string1==string2 form
of IF. These comparisons are generic, in that if both string1 and
string2 are both comprised of all numeric digits, then the strings are
converted to numbers and a numeric comparison is performed.
The CMDEXTVERSION conditional works just like ERRORLEVEL, except it is
comparing against an internal version number associated with the Command
Extensions. The first version is 1. It will be incremented by one when
significant enhancements are added to the Command Extensions.
CMDEXTVERSION conditional is never true when Command Extensions are
disabled.
The DEFINED conditional works just like EXIST except it takes an
environment variable name and returns true if the environment variable
is defined.
%ERRORLEVEL% will expand into a string representation of
the current value of ERRORLEVEL, provided that there is not already
an environment variable with the name ERRORLEVEL, in which case you
will get its value instead. After running a program, the following
illustrates ERRORLEVEL use:
goto answer%ERRORLEVEL%
:answer0
echo Program had return code 0
:answer1
echo Program had return code 1
You can also use numerical comparisons above:
IF %ERRORLEVEL% LEQ 1 goto okay
%CMDCMDLINE% will expand into the original command line passed to
CMD.EXE prior to any processing by CMD.EXE, provided that there is not
already an environment variable with the name CMDCMDLINE, in which case
you will get its value instead.
%CMDEXTVERSION% will expand into a string representation of the
current value of CMDEXTVERSION, provided that there is not already
an environment variable with the name CMDEXTVERSION, in which case you
will get its value instead.

Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?

A frequent method to handling errors within Windows batch scripts is to use things like
if errorlevel 1 ... or if %errorlevel% neq 0 .... Often times one wants the error handling code to preserve the ERRORLEVEL.
I believe all external commands will always result in ERRORLEVEL being set to some value, so the error handling code must preserve the ERRORLEVEL in an environment variable prior to executing an external command.
But what about internal commands? The problem is, some internal commands clear the ERRORLEVEL to 0 when they succeed, and some do not. And I can't find any documentation specifying which commands do what.
So the question is, which internal commands clear the ERRORLEVEL to 0 upon success? This is not a general question about returned ERRORLEVEL codes, but strictly about success results.
There are posts like What is the easiest way to reset ERRORLEVEL to zero? and Windows batch files: .bat vs .cmd? that give partial answers. But I have never seen a comprehensive list.
Note: I've been curious about this for years. So I finally decided to run a bunch of experiments and come up with a definitive answer. I'm posting this Q&A to share what I have found.
This answer is based on experiments I ran under Windows 10. I doubt there are differences with earlier Windows versions that use cmd.exe, but it is possible.
Also note - This answer does not attempt to document the ERRORLEVEL result when an internal command encounters an error (except for a wee bit concerning DEL and ERASE)
Not only are there difference between commands, but a single command can behave differently depending on whether it was run from the command line, or within a batch script with a .bat extension, or from within a batch script with a .cmd extension.
The following set of commands never clear the ERRORLEVEL to 0 upon success, regardless of context, but instead preserve the prior ERRORLEVEL:
BREAK
CLS
ECHO
ENDLOCAL
FOR : Obviously, commands in the DO clause may set the ERRORLEVEL, but a successful FOR with at least one iteration does not set the ERRORLEVEL to 0 on its own.
GOTO
IF : Obviously, commands executed by IF may set the ERRORLEVEL, but a successful IF does not set ERRORLEVEL to 0 on its own.
KEYS
PAUSE
POPD
RD
REM
RMDIR
SHIFT
START
TITLE
The next set of commands always clear the ERRORLEVEL to 0 upon success, regardless of context:
CD
CHDIR
COLOR
COPY
DATE
DEL : Always clears ERRORLEVEL, even if the DEL fails (except when run without any file argument).
DIR
ERASE : Always clears ERRORLEVEL, even if ERASE fails. (except when run without any file argument).
MD
MKDIR
MKLINK
MOVE
PUSHD
REN
RENAME
SETLOCAL
TIME
TYPE
VER
VERIFY
VOL
Then there are these commands that do not clear ERRORLEVEL upon success if issued from the command line or within a script with a .bat extension, but do clear the ERRORLEVEL to 0 if issued from a script with a .cmd extension. See https://stackoverflow.com/a/148991/1012053 and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J for more info.
ASSOC
DPATH
FTYPE
PATH
PROMPT
SET
Lastly, there are these commands that do not fit neatly into any of the prior categories:
CALL : If a :routine or batch script is CALLed, then ERRORLEVEL is exclusively controlled by the CALLed script or :routine. But any other type of successful CALL to a command will always clear ERRORLEVEL to 0 if the CALLed command does not otherwise set it.
Example: call echo OK.
EXIT : If used without /B, then the cmd.exe session terminates and there is no more ERRORLEVEL, just the cmd.exe return code. Obviously EXIT /B 0 clears the ERRORLEVEL to 0, but EXIT /B without a value preserves the prior ERRORLEVEL.
I believe that accounts for all internal commands, unless there is an undocumented command that I missed.
Your description of CALL command is incomplete:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it.
Example: call echo OK.
Check this small example:
#echo off
call :setTwo
echo Set two: %errorlevel%
call :preserve
echo Preserve: %errorlevel%
call echo Reset
echo Reset: %errorlevel%
call :subNotExists 2> NUL
echo Sub not exist: %errorlevel%
goto :EOF
:setTwo
exit /B 2
:preserve
echo Preserve
exit /B
Output:
Set two: 2
Preserve
Preserve: 2
Reset
Reset: 0
Sub not exist: 1
CALL description should say something like this:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it. Example: call echo OK, but if the called command is a subroutine it preserves the prior ERRORLEVEL. If the called subroutine does not exist, it sets the ERRORLEVEL to 1.

Is Windows %ERRORLEVEL% == $? in Linux

Trying my hands on windows batch files, in the below code that I found by searching in www.
#ECHO OFF
REM Call this with two arguments, and it will add them.
SET a=%1+%2
IF %ERRORLEVEL%==0 (goto errors-0) ELSE (goto errors-1)
REM Instead of using goto with the variable, this uses an IF-ELSE structure
:errors-0
REM This is if it was successful
ECHO %a%
goto exit
:errors-1
REM this is if it had an error:
ECHO Errors occurred.
goto exit
REM GUESS WHAT, THIS REM WILL NEVER EVER BE READ! IT WILL BE SKIPPED OVER BY THE GOTOS
:exit
ECHO.
ECHO press any key to exit.
PAUSE>nul
The code is suppose to take 2 arguments, add them and echo the result.
But this won't execute with success on my Windows 8.1 machine. Below is what I get:
C:\ProjectDoc>add3.bat
Errors occurred.
press any key to exit.
So, U added an echo for the ERRORLEVEL to see its value after executing the command SET. Below is the output:
C:\ProjectDoc>add3.bat 2 3
9009
Errors occurred.
press any key to exit.
C:\ProjectDoc>
So, is this errorlevel in Windows equal to the $? of Linux. Should it be returning 0 for every successful execution of a command or is it different? Reading some docs relates it to the Linux $? but it isn't clearly working as $? in Linux.
Yes, to be precise, %errorlevel% is analogous to $? in Bash shell.
In your batch file, SET a=%1+%2 is not doing what you expect it to do. It just sets the value of the variable a to the string "2+3" assuming you ran the file with arguments 2 3. If you want to do arithmetic you need to use the /A option: set /a a=%1+%2.
The SET command (and many other built-in commands) only set the ERRORLEVEL if there has actually been an error. If it was successful, the ERRORLEVEL will retain its previous value. I think this is what you're witnessing in your question.
By contrast, when a command runs an executable file, when the process completes it always sets the ERRORLEVEL.
As well as checking the ERRORLEVEL variable for specific values, it is idiomatic (for historical reasons) to check the errorlevel using the following expression
IF ERRORLEVEL 1 ECHO Hello
This will run the given command if ERRORLEVEL is 1 or above - in other words, if any error has occurred.

How can I display a description based on errorlevel value?

I'm writing a batch script to install an exe and a successful installation returns 0 and other codes mean different reasons.
I know I can print those error code(s) by echo %errorlevel%
Can I print the description related to the code instead? If so, how?
i.e. print 'successful' for code 0, etc.
Add below lines to print a message using error codes
if %errorlevel% equ 0 echo Successful
if %errorlevel% NEQ 0 echo Not Successful
Because errorlevel 0 indicates that return values of last executed command is successful,other return values have their own meaning
You can use || to act on failure, for simplicities sake this should do fine.
if %errorlevel% equ 0 echo Installed! || echo Install failed...
And && to activate on success.
del file.ext && echo File deleted.
The reason I don't provide a per errorlevel basis is that there can be up to 255 of them, something that would take an exceedingly high amount of time, and is further hindered by the tendency of programs not publically showing or ever documenting what each and every errorlevel means.
Something good to keep in mind is the difference between %errorlevel% and errorlevel. here

Resources