batch ERRORLEVEL always 0 - batch-file

I'm facing an issue when trying to implement the ERRORLEVEL on my batch script. Basically what I'm trying to do is: look for the .txt files in the directory and if found; .txt will get listed, if not found; message error will occur.
The thing is that this directory will not always contain a .txt, sometimes it will be empty and when that happens my code will not work. Only when there is a .txt in the directory I'm getting my conditions to work (ERRORLEVEL = 0), but if empty; none of my conditions will. Not even the ERRORLEVEL will be printed in the cmd screen (I should see it as ERRORLEVEL = 1).
This is my code:
for /r "C:\Texts" %%a in (*.txt) do (
echo %errorlevel%
IF ERRORLEVEL 0 (
echo %%a
echo "I found your Text!"
) ELSE (
echo "I couldn`t find your Text!" )
)
What exactly is wrong with my ERRORLEVEL implementation?

Errorlevel 0 is always true.
Use
if not errorlevel 1
But your code doesn't set the errorlevel.

In your code there is not any command that set the errorlevel. This value is set by all external .exe commands (like find or findstr), and by certain specific internal commands (like verify that set errorlevel=1 when its parameter is wrong, or ver that always set the errorlevel=0.
You may explicitly set this value in your code this way:
rem Set the errorlevel to 1
verify bad 2> NUL
for /r "C:\Texts" %%a in (*.txt) do (
echo %%a
rem Set the errorlevel to 0
ver > NUL
)
if not ERRORLEVEL 1 (
echo "I found your Text!"
) else (
echo "I couldn't find your Text!"
)
However, you may also get a similar result using a Batch variable instead of the errorlevel...

Related

Proper error return code?

I have a small problem with my batch file, here is my code:
Java -jar ****name.jar -commands****
So incase the user does have java installed it should give proper error code and if it has java installed and the java does not find the file its trying to run it should give proper error code and so on.
If "%Errorlevel%" NEQ "0" (
For /F "Tokens=*" %%C In ('Net Helpmsg %Errorlevel%') Do (
Echo Error Level: [Return Code #%Errorlevel% - %%C]
)
)
I was hoping it would get me correct error codes but it doesn't. Like for example if the user does not have java installed it would say something like this "java is not recognized.." and it should give an correct error code beneath it but unfortunately, it doesn't.
Here is what i have tried:
Removed the quotation around Errorlevel so its does not treat it like a string.
I was hoping anyone could point out my mistake?
The error numbers used by net helpmsg and ErrorLevel are totally different things; net helpmsg only understands error numbers returned by the net command.
What you are trying to accomplish is the following, I think (take a look at the explanatory remarks):
rem /* Test whether `java` is installed and can be found: */
rem // simply try to run the `java` executable:
java -version 2> nul
if ErrorLevel 1 echo ErrorLevel is set to %ErrorLevel% [9009 if not found, 0 otherwise].
rem // or use the `where` command to find the `java` executable:
where java > nul 2> nul
if ErrorLevel 1 echo ErrorLevel is set to %ErrorLevel% [1 if not found, 0 otherwise].
if ErrorLevel 1 goto :EOF
rem /* Thest whether the Java script `name.jar` exists: */
rem // simply try to run the Java script:
java name.jar -commands 2> nul
if ErrorLevel 1 echo ErrorLevel is set to %ErrorLevel% [1 if not found, or, if the script exists, its `ErrorLevel`].
rem // or use the `where` command to find the `java` executable:
where name.jar > nul 2> nul
if ErrorLevel 1 echo ErrorLevel is set to %ErrorLevel% [1 if not found, 0 otherwise].
rem /* (the `> nul` suffix suppresses output messages,
rem the `2> nul` suffix suppresses error messages) */
rem /* To capture the error message returned by any command, do this: */
for /F delims^=^ eol^= %%E in ('
command 2^>^&1 ^> nul
') do (
echo This is the error message: "%%E"
)
Since the question post has been updated and clarified, the following does not cover the problem. The true solution can be found above. I keep the former answer here for reference.
This is for the case you want to extract the error number returned by the net command, which is specific to this command and has got nothing to to with the ErrorLevel:
The error number of the net command and the ErrorLevel are totally different things.
Supposing you try to execute net view, it might fail with this error message, for instance:
System error 6118 has occurred.
The list of servers for this workgroup is not currently available
The resulting ErrorLevel is 2 though.
So executing net helpmsg %ErrorLevel% does not make any sense.
In contrast, net helpmsg 6118 does, which returns the following string:
The list of servers for this workgroup is not currently available
To capture the error number of the net command, use a for /F loop and take the line of the first iteration only by an if defined condition (using command net view in this example):
set "ERRNUM=" & set "ERRTXT="
for /F "delims=" %%M in ('2^>^&1 ^> nul net view') do (
rem Note that the `token` option is language-dependent:
for /F "tokens=3" %%N in ("%%M") do (
if not defined ERRNUM set "ERRNUM=%%N"
)
set "ERRTXT=%%M"
)
This would capture the error number 6118 of the above example and store it in variable ERRNUM. The last line of the error output (that I consider as the error message) is stored in variable ERRTXT.
To retrieve the error message later from a given error number stored in ERRNUM, you could use this:
set /A "ERRNUM+=0"
if %ERRNUM% NEQ 0 (
for /F "delims=" %%M in ('net helpmsg %ERRNUM%') do (
set "ERRTXT=%%M"
)
)
Side Note: Yes, I would remove the quotes if %ErrorLevel% NEQ 0 to do a numeric comparison.

'for' loop variable not releasing on loop iterations

Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...

batch if statment return same result in any case

in C:\Easy_ERROR there is only 3 files. when only in 1 file you can find the string 'alexm'
#echo off
#break off
#color 0a
#cls
FOR %%a IN (C:\Easy_ERROR\EIM*.txt) DO (
find /c /i "ALEXM" C:\Easy_ERROR\%%~nxa
IF %errorlevel% EQU 0 ECHO FOUND
)
)
pause
exit
when you run it, the statement:
IF %errorlevel% EQU 0 ECHO FOUND
always writing me "FOUND" for all 3 files!
its only an exemple for something else that im trying to do. but the same case!
Variables are expanded at load time. Your entire FOR construct is loaded as 1 line... regardless of how many lines it contains. So the value for %errorlevel% is NOT the run time value that you are expecting. Replace this line
IF %errorlevel% EQU 0 ECHO FOUND
with this
IF not errorlevel 1 ECHO FOUND
See IF /?
Note that this specifies a true condition if errorlevel is NOT equal to or greater than 1. So this would not work if a negative value was returned, as it too is less than 1.

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
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.
#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 ECHOed 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 ECHOed as I mentioned earlier.
and output.txt
seterr1.bat, J1, K1
seterr5.bat, J2, K2
seterr5.bat, J4, K4
notexist.bat, J5, K5
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.
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.
For posterity, when specifically setting it to 0, I like
ver >nul
ver.exe always returns 0.
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
)
)
)
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

How does a Windows batch file detect that a file is > 0 bytes?

I have a Windows batch file utilized in my Visual Studio tool chain that creates a list of files in a particular directory, and then uses "findstr" to narrow this list down to only the files whose names contain a particular string; and then does some work on these files.
dir /b \mypath\*.wav >wavRawList.txt
findstr /b /v "DesiredString" wavRawList.txt >wavListWithDesiredString.txt
for /f %%a in (wavListWithDesiredString.txt) do (
[... do some stuff ...]
)
Visual Studio frequently reports errors from this batch file, and I think it's because wavListWithDesiredString.txt frequently ends up being a file with a length of 0. Is there a variety of "if exist wavListWithDesiredString.txt" where instead of "exist" I can substitute a command meaning "if it exists and its file length is greater than 0"?
The more-or-less inline way, using for:
for %%x in (wavListWithDesiredString.txt) do if not %%~zx==0 (
...
)
or you can use a subroutine:
:size
set SIZE=%~z1
goto :eof
which you can call like this:
call :size wavListWithDesiredString.txt
if not %SIZE%==0 ...
IF EXIST %1 IF %~z1 GTR 0 ECHO Both conditions are satisfied.
Does not work, because if file does not exist this part:
"IF %~z1 GTR 0" resolves to "IF GTR 0" which is invalid command and results in:
0 was unexpected at this time.
Delayed expansion does not help either. To fix use this:
if exist %1 (
echo "Ouput file exists, checking size"
for %%x in (%1) do if not %%~zx==0 (
echo "File exists, size is non zero"
... DO SOMETHING ...
) else (
echo "Size is zero"
)
) else (
echo "There is no file"
)
I was able to solve the file size part of the question with another stack overflow question found here. I would use nesting to simulate the AND operator like the example below:
IF EXIST %1 IF %~z1 GTR 0 ECHO Both conditions are satisfied.
The %1 is a parameter that must be passed into a batch file.

Resources