Proper error return code? - batch-file

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.

Related

WinScp - Difficulty with handling error values

i am trying to write a simple winscp script to use it inside a for loop, in a batch file. The loop is :
for /F "tokens=2 delims==" %%s in ('set REMOTE_PATH[') do ("
ECHO CHECKING FOR %%s
winscp.exe /console /script=run01test1 /parameter // %%s
IF %ERRORLEVEL% neq 0 (
ECHO FILE %%s DOES NOT EXIST
CALL:error10 "76" "UNABLE TO LOCATE TARGET FILE %%s "
CALL:ERRORNOTIFYMAIL "UNABLE TO LOCATE TARGET FILE %%s" "FATAL" "CHECK EXISTENCE OF TARGET DIRECTORY" "77"
EXIT /B 10
) ELSE (
ECHO FILE %%s EXISTS
)
ECHO.
)
and the run01test1 winscp script is :
open sftp://xxx:xxx#xx.xx.xx.xx
stat "%1%"
exit
My problem is, that it either doesnt pass the parameter right, or for some reason the return value always returns zero whether the command stat succeeds or not. For the record, the idea is to check if files exist, the files are stored in an array in the style of :
set remote_path[0]
set remote_path[1]...
Can anyone help? Thanks in advance
You've got an delayed expansion problem with your %errorlevel% variable.
Either enable delayed expansion with setlocal enabledelayedexpansion and use !errorlevel! inside your code block, or use an alternative syntax:
if errorlevel 1 (...
which checks if errorlevel is 1 or higher. (see if /? for description)

How to search a text file in batch for a specific symbol and alter the script depending on the result

I need to create a command that allows me to insert a check of a text file for a very specific symbol (’) and I am having trouble. It is a single quotation mark and it occasionally is found on some folders that need to be zipped and when my batch zipper encounters the folder with the symbol in it's name, it just starts having a lot of problems and creates weird files. I am not going into a lot of detail, but I just need a way to (in plain terms) check if a text file contains the symbol (’) and if it does, send the script to an error line (just something to indicate the symbol was found, like "echo error found"). And if not, then just send it to the rest of the script...
Like FINDSTR "’" dirlist.txt
if found goto err else goto resume
I know that is very incorrect but you get the idea.
Here is what I have so far and I still have made no progress getting it to work:
findstr /i /c:"’" C:\ACFZ\FORZIP\dirlist.txt >2
if %errorlevel% EQU 0 (goto LABEL0) else (goto LABEL1)
:LABEL0
msg %username& "An invalid symbol has been found. Remove any single quotation marks (’) from the folder names and try again. If unsure, simply remove anything that looks like an apostrophe."
pause
goto ERROR
:LABEL1
echo No errors found, continuing
pause
goto ZIPSTART
:ERROR
echo an error was found, exiting...
pause
goto EXIT
It always ends up saying no errors, even though the file has the symbol in it.
Here is the text file I need to search (dirlist)
2082708 Amboy Bank
2082712 Cavender’s
2082736 Elizabeth Board of Education
2082763 Tri-Valley Developmental Services LLC
2082773 Vector Management
OK, so I finally got it working right... Thanks to Harvey, I used the method of outputting any results to a separate file, and then checking that file for contents. Which actually works great, because if it finds an issue, it will show you the full name of the problem folder(s) so you can easily fix it.
Here is the snippet of the working part:
findstr "'" C:\ACFZ\FORZIP\dirlist.txt > error.txt
findstr "." error.txt >nul
IF %ERRORLEVEL% EQU 0 GOTO POPUP
IF %ERRORLEVEL% EQU 1 GOTO ALLCLEAR
and here it is with a bit more detail:
CD C:\ACFZ\FORZIP
DIR /AD /B /ON >dirlist.txt
Echo Checking for errors in folder names...
ping -n 3 localhost >nul
REM that is not an apostrophe!
findstr "'" C:\ACFZ\FORZIP\dirlist.txt > error.txt
findstr "." error.txt >nul
IF %ERRORLEVEL% EQU 0 GOTO POPUP
IF %ERRORLEVEL% EQU 1 GOTO ALLCLEAR
REM Errorlevel 0= Something found, 1= nothing found
:POPUP
color cf
msg %username% "An invalid symbol has been found. Remove any single quotation marks (’) from the folder names and try again. If unsure, simply remove anything that looks like an apostrophe."
goto ERROR
:ALLCLEAR
echo No errors found, continuing...
ping -n 3 localhost >nul
ping -n 3 localhost >nul
goto ZIPSTART
:ERROR
echo An error was found in the following folder name(s) below:^
findstr "." error.txt
echo.
Echo Remove any symbols from the above folder name(s)
echo within your completed folder and try again.
Echo This program will now exit.
pause
goto EXIT
:ZIPSTART
REM Zip contents of each directory
for /f "tokens=*" %%a in (dirlist.txt) do (
CD "%%a"
wzzip "C:\ACFZ\ZIPPED\%%a.zip"
CD..
)
Glad I was able to fix this. I guess WinZip goes really crazy from that quotation mark. The reason I needed this was I wrote this batch script (there is more to it than what I have above, as this was the part I needed to work on) to automate the zipping and backup process at my work, so that the folders for the month's jobs are zipped up and then copied onto the server for archive. It was a pain to manually do it, so with this I can just do it all in one step.
Oh and yeah the errorlevel issue was I did not have it entered correctly. I did not space them over to the right.
Thanks to all who helped.
%error_level% indicates the status of the execution which always successful (0) unless you pass in wrong arguments (e.g. try run findstr without argument or with a wrong file name).
In your case, you need to examine the output (messages printed on the screen) of findstr. One approach is to rely on the fact that nothing is printed on the screen if findstr finds no string matched the search. For example:
set found=""
findstr "'" C:\ACFZ\FORZIP\dirlist.txt > findresult.txt
call:CheckEmptyFile findresult.txt found
if "%found%" EQU "FOUND" (
echo An invalid symbol has been found
) else (
echo No errors found, continuing
)
REM your execution goes here
REM Clean up
del findresult.txt
goto :eof
:CheckEmptyFile
if %~z1 == 0 (
set "%~2=NOTFOUND"
) else (
set "%~2=FOUND"
)
goto :eof
(Reference: Windows BAT : test if a specific file is empty)

Batch file fault or cl.exe lying for compilation result/exit

I'm writing a batch file that tests a header policy (each header must include/resolve its own dependencies), but it appears cl.exe is returning success even though it is actually failing..
Script without comments is:
#echo off
set FNAME=temp
set OBJFILE=%FNAME%.obj
set SRCFILE=%FNAME%.cc
for /f "delims=|" %%i in ('dir /b /s ..\include\*.h') do (
( echo #include "%%i" & echo void test^(^){} ) > %SRCFILE%
echo %%i
"%VCINSTALLDIR%\bin\cl.exe" /c /W4 %SRCFILE% > NUL 2>&1
if not ERRORLEVEL 0 goto failed
)
goto success
:failed
echo.
echo Compile failed.
goto fin
:success
echo.
echo Success.
goto fin
:fin
if exist %OBJFILE% del %OBJFILE% > NUL
if exist %SRCFILE% del %SRCFILE% > NUL
I suspect the fault lies with the errorlevel detection (I've read raymond chens article, as well as other SO posts about its caveats), but all attempted variations have also failed in the same way, making me think cl.exe is lying. I could be completely wrong, so I was going to check with ProcMon - but sadly it's crashing at the moment.
Is it just me being stupid?
I'm currently forcing failure in a header file using FakeType blah;, which cl.exe does output if I redirect to file:
...\include\fail.h(1) : error C2146: syntax error : missing ';' before identifier 'blah'
...\include\fail.h(1) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
The programs usually return an error code via an ERRORLEVEL value greater than zero; however, cl.exe may return positive or negative values of ERRORLEVEL when there is an error, and a value of zero if it is OK. The usual form:
if errorlevel num ...
is true if the errorlevel is greater or equal than the given number, so
if not ERRORLEVEL 0 goto failed
will be true when the errorlevel is less than zero. There are two ways to test if errorlevel is exactly zero:
if errorlevel 0 if not errorlevel 1 goto success
that is, if errorlevel is greater or equal zero and less than 1. Perhaps the clearest way is to directly compare the errorlevel value:
if !errorlevel! equ 0 goto success
Remember that this form requires setlocal EnableDelayedExpansion command at beginning.

batch ERRORLEVEL always 0

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...

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

Resources