ERRORLEVEL inside IF - batch-file

Just stumbled into a weird thing with %ERRORLEVEL% and wanted to see if anyone knows why and if there's a way to fix it. Essentially, it seems as if commands executed inside if statements don't set the %ERRORLEVEL% variable. The ERRORLEVEL (as in IF ERRORLEVEL 1, which is different from IF %ERRORLEVEL% EQU 1 ) check seems to still work fine though, so I can probably work around it, but it would still be nice to be able to print the error level. For debugging or whatever.
#echo off
Set TESTVAR=1
tasklist | find /I "IsntRunning.exe" > NUL
echo OUTSIDE_IF %ERRORLEVEL%
ThisWillSetErrorLevelTo9009ieNotRecognizedCommand
tasklist | find /I "IsntRunning.exe" > NUL
echo OUTSIDE_IF %ERRORLEVEL%
ThisWillSetErrorLevelTo9009ieNotRecognizedCommand
IF %TESTVAR% EQU 1 (
Set ERRORLEVEL=
tasklist | find /I "IsntRunning.exe" > NUL
echo INSIDE_IF ERRORLEVEL %ERRORLEVEL%
IF ERRORLEVEL 1 (
echo INSIDE_IF2 ERRORLEVEL GREQ 1 %ERRORLEVEL%
)
IF ERRORLEVEL 2 (
echo INSIDE_IF2 ERRORLEVEL GREQ 2 %ERRORLEVEL%
)
IF ERRORLEVEL 3 (
echo INSIDE_IF2 ERRORLEVEL GREQ 3 %ERRORLEVEL%
)
)
tasklist | find /I "IsntRunning.exe" > NUL
echo OUTSIDE_IF ERRORLEVEL %ERRORLEVEL%
#echo on
Putting that in a batch file and running it produces this output:
C:\Users\username\Documents\work>test.bat
OUTSIDE_IF 1
'ThisWillSetErrorLevelTo9009ieNotRecognizedCommand' is not recognized as an internal or external command,
operable program or batch file.
OUTSIDE_IF 1
'ThisWillSetErrorLevelTo9009ieNotRecognizedCommand' is not recognized as an internal or external command,
operable program or batch file.
INSIDE_IF ERRORLEVEL 9009
INSIDE_IF2 ERRORLEVEL GREQ 1 9009
OUTSIDE_IF ERRORLEVEL 1
Relevant articles:
http://blogs.msdn.com/b/oldnewthing/archive/2008/09/26/8965755.aspx
http://support.microsoft.com/kb/69576

Try using setlocal enabledelayedexpansion at the start of your batch file, and !ERRORLEVEL! inside your IF. This seems to work for me:
#echo off
setlocal enabledelayedexpansion
dir nul
echo %ERRORLEVEL%
if .1.==.1. (
urklbkrlksdj - not a command
echo %ERRORLEVEL%
echo !ERRORLEVEL!
)

if errorlevel works without delayed expansion but works in manner similar to
if %errorlevel% <= Some_Value ... :
#echo off
::sets errorlevel to 0
(call )
if "1" == "1" (
rem sets errorlevel to 5
cmd /c exit 5
if errorlevel 4 echo this will be printed
if errorlevel 5 echo this will be printed
rem :::: you can use this ::::::::::::::
if errorlevel 5 if not errorlevel 6 echo this will be printed ONLY when the errorlevel is 5
rem :::::::::::::::::::::::::::::::::::::
if errorlevel 6 echo this will not be printed
)

Related

Check whether a process is running and terminate it if it's exceeded time limit

So I'm working a project that make competitive judger from batch file, and it works well with correct and incorrect answer. But I stuck with my problem when I try to measure time execution this code. I used timeout and taskkill to delay it for an amount of time, and kill it if didn't finish.
But I faced two problem:
My code process can be killed twice, and it can crash my batch file.
It denied my input and output file, so I can't compare my file for verdict.
So how can I measure time, kill a process on Windows and get rid all of my problem?
Here is my code:
main.bat
#echo off
:init
set home=%cd%
chcp 65001>nul
set total=0
set pass=0
:input
set /p user="Enter username: "
if not exist .\submits\%user% (
echo [[91mERROR[0m] Invalid username.
pause 0
exit /b 1
)
set /p prob="Problem: "
if not exist .\problems\%prob% (
echo [[91mERROR[0m] Invalid problem.
pause 0
exit /b 1
)
if not exist .\submits\%user%\%prob%.cpp (
echo [[91mERROR[0m] User haven't submited this problem yet.
pause 0
exit /b 1
)
:judge
rem **compile file, setup for judging**
echo [[93mJury[0m] Judging problem %prob% from user %user%...
if exist result.log del /q result.log
cd submits\%user%
g++ %prob%.cpp -o %prob%.o -std=c++14 -O2
if not errorlevel 0 (
echo [91mERROR[0m Compile failed...
pause 0
exit /b 1
)
move %prob%.o "%home%">nul
cd %home%\problems\%prob%\tests
for /d %%i in ("*") do (
set /a total+=1
rem echo [[93mJury[0m] Test %%i...
copy "%%i\*.txt" "%home%">nul
cd %home%
ren out.txt ans.txt
rem more ans.txt
echo|set /p=Test %%i: >>result.log
judge %prob%.o
if errorlevel 0 set /a pass+=1
del /q *.txt
cd %home%\problems\%prob%\tests
)
cd %home%
echo [[92mOK[0m] Judging completed, user passed %pass%/%total% test(s), please check result.log for more detail.
del /q %prob%.o
pause 0
judge.bat
rem #echo off
start %1
timeout /t 1 /nobreak
taskkill /im %1 /f
rem echo %errorlevel%
if not errorlevel 128 (
echo TLE.>>result.log
exit /b 1
)
fc /a /w out.txt ans.txt>nul && (
echo Correct.>>result.log
exit /b 0
) || (
echo Wrong Answer.>>result.log
exit /b 1
)
(I know my English is so bad, so I can't explain all of my problem, if you have any question, feel free to ask me, I will answer for you)
In
echo|set /p=Test %%i: >>result.log
judge %prob%.o
if errorlevel 0 set /a pass+=1
You are executing judge which appears to be a batch file.
You need
CALL judge %prob%.o
in order that processing will continue in your main procedure after judge ends. As it is, processing will be switched to judge.bat and end when judge.bat ends.
if errorlevel 0 set /a pass+=1
IF ERRORLEVEL n is TRUE if the runtime (ie. current) errorlevel is n or greater than n. IF ERRORLEVEL 0 is therefore always true. IF NOT ERRORLEVEL 1 is a test for errorlevel=0.
Hope this helps a bit - not really sure about your other problems.

Windows batch file for invalid microsoft updates

I am struck while creating a windows batch file which just indicates if an invalid KB article is installed on my computer/ windows server.
This is where i am at now,
Script :
#ECHO OFF
WMIC QFE GET HOTFIXID>%~dp0QFE_list.txt
FOR /f "delims=," %%a IN (%~dp0Patch_List.txt) DO (
CALL :PATCH_LIST %%a
)
GOTO :EOF
:PATCH_LIST
REG QUERY "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s /f "%1">NULL.txt
IF %ERRORLEVEL% EQU 0 ECHO %1: INSTALLED
IF %ERRORLEVEL% NEQ 0 (
ECHO FIND %1
FIND /C "%1" %~dp0QFE_List.txt>NULL.txt
IF ERRORLEVEL 0 ECHO %1: QFE INSTALLED
IF ERRORLEVEL 1 ECHO %1: **** NOT INSTALLED! ****
)
Current output : ---------- C:\USERS\PVENK17\DESKTOP\TEST\QFE_LIST.TXT: 1
Desired output : : Installed
Inputfile contents : KB3057839,KB3002657
Issue :
Even though it works for 1 KBarticle. When i place more than 1 in the inputfile it is not working.
Kindly help me resolve this issue.
Thanks
Prashanth
I can make this work by changing the input file format: 1 KB per line
KB3057839
KB3002657
Then, just remove the "delims" stuff and it works for several items. Not sure of the logic of the last lines. It seems to say "installed/not installed"
And BTW redirect your commands to NUL to avoid creating a useless file.
#ECHO OFF
WMIC QFE GET HOTFIXID>%~dp0QFE_list.txt
FOR /f %%a IN (%~dp0Patch_List.txt) DO (
CALL :PATCH_LIST %%a
)
GOTO :EOF
:PATCH_LIST
REG QUERY "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s /f "%1">NUL
IF %ERRORLEVEL% EQU 0 ECHO %1: INSTALLED
IF %ERRORLEVEL% NEQ 0 (
ECHO FIND %1
FIND /C "%1" %~dp0QFE_List.txt>NUL
IF ERRORLEVEL 0 ECHO %1: QFE INSTALLED
IF ERRORLEVEL 1 ECHO %1: **** NOT INSTALLED! ****
)
After much more debugging i wrote this code, i know its not ideal solution but it works
Code :
#ECHO OFF
title IllegalPatchCheck
echo Select a server. (AW/PG)
set /p server=
IF /i "%server%"=="AW" goto AdminWorkstation
IF /i "%server%"=="PG" goto PeripheralGateway
echo Invalid Input.
goto commonexit
:AdminWorkstation
WMIC QFE GET HOTFIXID>%~dp0QFE_list.txt
find /c "KB3057839" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 ECHO KB3057839 NOT Found
if %errorlevel% neq 1 ECHO KB3057839 Found
find /c "KB3058515" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 ECHO KB3057839 NOT Found
if %errorlevel% neq 1 echo KB3058515 Found
find /c "KB3059317" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 echo KB3059317 NOT found
if %errorlevel% neq 1 echo KB3059317 Found
find /c "KB3063858" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 echo KB3063858 NOT found
if %errorlevel% neq 1 echo KB3063858 Found
goto commonexit
:PeripheralGateway
WMIC QFE GET HOTFIXID>%~dp0QFE_list.txt
find /c "KB2984972" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 echo KB2984972 NOT Found
if %errorlevel% neq 1 echo KB2984972 Found
find /c "KB3046049" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 echo KB3046049 NOT Found
if %errorlevel% neq 1 echo KB3046049 Found
find /c "KB3002657" %~dp0QFE_list.txt>nul
if %errorlevel% equ 1 echo KB3002657 NOT Found
if %errorlevel% neq 1 echo KB3002657 Found
goto commonexit
:commonexit
del /q /f %~dp0QFE_list.txt >nul
pause
Thanks
Prashanth
#echo off
title HOTFIXID_KB_FOUND
setlocal enabledelayedexpansion
cd /d "%~dp0"
for /f %%A in (KB_list.txt) do (
wmic qfe get hotfixid |findstr /i "%%A"
if !errorlevel! equ 0 echo %%A: ****INSTALLED****
if !errorlevel! equ 1 echo %%A: NOT INSTALLED
)
pause
KB_list.txt, in my case, is the list of the harmful updates leading to BSOD. You can, of course, automate and delete, but not always get wusa.exe sometimes requires dism.exe.
An example of removing using wusa.exe
wusa.exe /uninstall /kb:3065987 /quiet /norestart
An example of removing using dism.exe
DISM /Online /Get-Packages /Format:Table
DISM /Online /Remove-Package /PackageName:Package_for_KB3045999~31bf3856ad364e35~amd64~~6.1.1.1

returning errorlevel from batch function doesnt work

I have some function in batch script:
:run
set "CMD=%*"
<...>
timeout 300s !CMD!
if %errorlevel% equ 0 (
echo !CMD! >> ./!_tool!.OK
) else (
echo !CMD! >> ./!_tool!.FAIL
echo exitcode= %errorlevel% >> ./FAIL
echo ===STOP=== %date% %time%
exit /b %errorlevel%
)
exit /b %errorlevel%
and im checking its %errorlevel% code in the main cycle:
for /f "tokens=*" %%t in (%TEST_LIST%) do (
<...>
call :run %TOOL% -O0 -S %REPO_PATH%\%%t
if %errorlevel% equ 0 (
echo %%t PASSED
) else (
echo %%t FAILED
)
But the issue when timeout 300s !CMD! returns errorlevel 1 and is returning exit /b %errorlevel% as 1 (./!_tool!.FAIL being created and so on) doesnt affect the main cycle's IF and im getting echo %%t PASSED anyway.
Cannot function return code be checked this way or what?
P.S. Some <...> code is working correctly, so I've cut it
Thanks to #Stephan i've found my problem - I should use !errorlevel! instead of %errorlevel% in my FOR loop as it should refresh every iteration

Batch errorlevel is not working properly

so i have really problems with the ERRORLEVEL of batch. Its just not working for me.
I have a big own ms build batch script and i always get 0 back from ERRORLEVEL, whatever I do (eg. msbuild, tf get, tf checkout, copy, xcopy,...)
so i did a small example to post it here:
#echo off
set Update=1
IF %Update% == 1 (
echo.
set /p "=- Copy stuff..." <NUL
xcopy /R /Y C:\test\2.lib C:\test1
if %ERRORLEVEL% neq 0 (echo FAILED!) ELSE (echo SUCCEED!)
echo -^> done
pause
)
so its always returning succeed and printing 0 when i do: echo %ERRORLEVEL%
can you please help me with that? I really would like to use that errorlevel
you need delayed expansion here or to use IF ERRORLEVEL :
#echo off
set Update=1
IF %Update% == 1 (
echo.
set /p "=- Copy stuff..." <NUL
xcopy /R /Y C:\test\2.lib C:\test1
IF ERRORLEVEL 1 (echo FAILED!) ELSE (echo SUCCEED!)
echo -^> done
pause
)
with IF ERRORLEVEL 1 you can check if the errorlevel is 1 or bigger .
As npocmaka says, you have a delayed expansion issue.
An alternative is to ditch ERRORLEVEL and use the && and || conditional command concatenation operators instead.
#echo off
set Update=1
IF %Update% == 1 (
echo.
set /p "=- Copy stuff..." <NUL
xcopy /R /Y C:\test\2.lib C:\test1 && (echo SUCCEED!) || (echo FAILED!)
echo -^> done
pause
)
Edit showing use of multiple lines
#echo off
set Update=1
IF %Update% == 1 (
echo.
set /p "=- Copy stuff..." <NUL
xcopy /R /Y C:\test\2.lib C:\test1 && (
echo First success command
echo SUCCEED!
) || (
echo First failure command
echo FAILED!
)
echo -^> done
pause
)

Using CHOICE with CMD script

Having trouble getting this CHOICE script to work. Can anyone provide any insight?
#echo off
CHOICE /C:IRCQSH /T 10 /N /M "Waiting with choice..." /D H
IF ERRORLEVEL 0 ECHO "Default choice: Health"
IF ERRORLEVEL 1 ECHO "Install"
IF ERRORLEVEL 2 ECHO "Remove"
IF ERRORLEVEL 3 ECHO "Console"
IF ERRORLEVEL 4 ECHO "Quit"
IF ERRORLEVEL 5 ECHO "Start"
IF ERRORLEVEL 6 ECHO "Health"
pause
You need to change your syntax to treat ERRORLEVEL as a variable, and use the CMD equality statements, such as:
IF %ERRORLEVEL% EQU 0 ECHO "Default choice: Health"
IF %ERRORLEVEL% EQU 1 ECHO "Install"
IF %ERRORLEVEL% EQU 2 ECHO "Remove"
IF %ERRORLEVEL% EQU 3 ECHO "Console"
IF %ERRORLEVEL% EQU 4 ECHO "Quit"
IF %ERRORLEVEL% EQU 5 ECHO "Start"
IF %ERRORLEVEL% EQU 6 ECHO "Health"
The reason your code is failing is, taken from here:
IF ERRORLEVEL n statements should be read as IF Errorlevel >= number
i.e.
IF ERRORLEVEL 0 will return TRUE when the errorlevel is 64
A couple points here:
The default choice does NOT return an ERRORLEVEL of zero, but the number of the choice selected. In your case, that is H, the default is equal to press H with an ERRORLEVEL of 6
The right way to take the value of ERRORLEVEL is enclosing it in percents and use the EQU comparison, as LittleBobbyTables said in his answer. However, there are other ways to achieve the same result.
The IF ERRORLEVEL Number Command test if the errorlevel value is Greater or Equal than the given number, so you may also use this form:
.
#echo off
CHOICE /C:IRCQSH /T 10 /N /M "Waiting with choice..." /D H
FOR %%E IN (6 5 4 3 2 1) DO IF ERRORLEVEL %%E GOTO LABEL-%%E
:LABEL-1
ECHO "Install"
GOTO CONTINUE
:LABEL-2
ECHO "Remove"
GOTO CONTINUE
:LABEL-3
ECHO "Console"
GOTO CONTINUE
:LABEL-4
ECHO "Quit"
GOTO CONTINUE
:LABEL-5
ECHO "Start"
GOTO CONTINUE
:LABEL-6
ECHO "Health"
:CONTINUE
pause
Perhaps the simplest way to achieve the same thing is defining an array and show the appropriate element using the errorlevel value as index:
.
#echo off
setlocal EnableDelayedExpansion
rem Create an array with the desired messages (selected by numeric index)
set index=0
for %%a in ("Install" "Remove" "Console" "Quit" "Start" "Health") do (
set /A index+=1
set elem[!index!]=%%a
)
CHOICE /C:IRCQSH /T 10 /N /M "Waiting with choice..." /D H
echo !elem[%ERRORLEVEL%]!
pause
For a further description of Batch arrays, see: Arrays, linked lists and other data structures in cmd.exe (batch) script

Resources