Batch Script not raising ERRORLEVEL on failure - batch-file

I am learning Windows batch scripting. I need to raise errors when copy, move or delete operations fail. I have made a sample script and for some reason, when the operation fails I cannot get the ERRORLEVEL to rise. I run the script and the files either do not exist or are opened in another program and stderr messages are output to console, but ERRORLEVEL never rises, why is this? Also, is there any way to pipe stderr into a variable I could check, if I cannot get ERRORLEVEL to rise?
My code is as below:
`#Echo Off
ECHO.
Set /P myVar=Please enter a value:
Echo var = %myVar%
ECHO Trying to delete the file dummy.csv >> log.txt
Set myVar2 = nothing
DEL C:\dummy420.csv
IF NOT ERRORLEVEL 0 ECHO There was an error
ECHO %ERRORLEVEL%
REM 2>myVar2
REM Echo 2>
REM Echo %myVar2%
Echo 2>&1
REM && (echo yourCommand was successful) || (echo yourCommand failed)
IF NOT ERRORLEVEL 0 ECHO There was an error
ECHO %ERRORLEVEL%
Move ""C:\dummy420.csv" D:\"
IF NOT ERRORLEVEL 0 ECHO There was an error
Set /P dummy=Press Enter to End`
This is my output:
Please enter a value:hello
var = hello
C:\dummy420.csv
The process cannot access the file because it is being used by another process.
0
ECHO is off.
0
The filename, directory name, or volume label syntax is incorrect.
Press Enter to End

DEL command does not change the errorlevel - more info
Check the debenham suggestion to detect failed deletion:
3>&2 2>&1 1>&3 del C:\dummy420.csv|findstr . && echo DEL failed || echo DEL succeeded
to change errorlevel:
3>&2 2>&1 1>&3 del C:\dummy420.csv|findstr .&& cmd /c exit /b 1

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.

Why isn't error triggered IF/THEN working correctly?

Coding is just something I like to play with so I have limited knowledge and research hasn't helped. If I run the script I get both success and error messages instead of one or the other.
#ECHO OFF
SET /p HOSTFILE=Host File Name?&CLS
SET /p HIDEFILE=File To Hide?&CLS
SET /p OUTPUT=Output File Name?&CLS
SET /p EXTENSION=Output File Extension (.Zip, .Jpg, .M4A)?&CLS
COPY /b "%HOSTFILE%" + "%HIDEFILE%" %OUTPUT%%EXTENSION% >null
if %ERRORLEVEL% EQU 0 GOTO continue
if %ERRORLEVEL% EQU 2 GOTO error
:continue
ECHO %OUTPUT%%EXTENSION% Created.
:error
ECHO File Not Created.
TIMEOUT /T 2 >null
As Compo said in the comment you should probably check if your files exist.
when both hostfile and hidefile exist, a success message is indicated otherwise error message follows.
#ECHO OFF
SET /p HOSTFILE=Host File Name?&CLS
SET /p HIDEFILE=File To Hide?&CLS
SET /p OUTPUT=Output File Name?&CLS
SET /p EXTENSION=Output File Extension (.Zip, .Jpg, .M4A)?&CLS
IF EXIST "%HOSTFILE%" IF EXIST "%HIDEFILE%" (
echo exist hidefile
COPY /b "%HOSTFILE%" + "%HIDEFILE%" %OUTPUT%%EXTENSION% > nul
GOTO continue
)
) ELSE (
echo does not exist
GOTO error
)
:continue
ECHO %OUTPUT%%EXTENSION% Created.
GOTO timer
exit /b 0
:error
ECHO File Not Created.
GOTO timer
exit /b 0
:timer
TIMEOUT /T 2 > nul
exit /b 0

SET /P Not Working After Checking For Administrator Privileges

I'm trying to force the user to run the script using administrative privileges:
#echo off
net session >nul 2>&1
if %errorlevel% == 0 (
echo Success: Administrative permissions confirmed.
echo.
echo Please Choose:
echo 1. Enable
echo 2. Disable
echo 3. Exit
set /p choice="> "
echo Choice: "%choice%"
) else (
echo Failure: Please run as administrator
)
pause
The net session and errorlevel lines check the privilege, and this works. The weird thing is that no matter what gets entered for choice it behaves as if it was never initialized.
The code running inside the if block works fine by itself, so I suspect the issue has something to do with how I'm checking for privileges.
Can someone explain this behavior, and any fixes?
You'd need either delayed expansion or avoid it by reorganizing your code a bit, like:
#echo off
net session >nul 2>&1
if %errorlevel% == 1 (
echo Failure: Please run as administrator
exit 1
)
echo Success: Administrative permissions confirmed.
etc.
The issue is because you are setting a variable within an If block, without delaying variable expansion.
However, you don't need the if block at all, nor do you need to set a variable for the selection.
The following method uses the || which essentially means 'if previous command produced an error', replaces the Set /P command with Choice and uses timed pauses via the Timeout command:
#Echo Off
Net Session >Nul 2>&1 || (
Echo Failure: Please run as administrator
Timeout 3 /NoBreak >Nul
Exit /B
)
Echo Success: Administrative permissions confirmed.
Echo=
Echo 1. Enable
Echo 2. Disable
Echo 3. Exit
Choice /C 123 /M "Please Choose"
If ErrorLevel 3 Exit /B
If ErrorLevel 2 GoTo Disable
Echo Enabling
Timeout 3 /NoBreak >Nul
Exit /B
:Disable
Echo Disabling
Timeout 3 /NoBreak >Nul
Exit /B

How to ensure every command is Successful in batch script

I have a batch file with 10 lines and 5 functions in a batch script. How can I ensure that all the commands in a batch file are successful.
In other way, what's the logic to calculate return code of each command at the end of a script.
1. #ECHO OFF
2. if not exist "%Destination%\%NAME%" md %Destination%\%NAME%
3. if not exist "%Destination%\%NAME2%" md %Destination%\%NAME2%
4. rmdir %Destination%\%NAME3%
5. if not exist "%Destination%\NAME4%" md %Destination%\%NAME4%
6. cd /d X:\test1
in the above 5 lines, 4th line returns %ERRORLEVEL% 1 and 6th line returns the same. But, I could not put IF %ERRORLEVEL%==0 after every command. So, how could i script to handle this.
You should firstly save your file as .cmd instead of .bat for better error handling. Also always enclose your paths with double quotes. Then I suggest you test existance as well to overcome errorlevel.
If exist "%Destination%\%NAME3%" rmdir "%Destination%\%NAME3%"
For the code example I suggest following:
#echo off
rem Verify the existence of all used environment variables.
for %%I in (Destination NAME NAME2 NAME3 NAME4) do (
if not defined %%I (
echo Error detected by %~f0:
echo/
echo Environment variable name %%I is not defined.
echo/
exit /B 4
)
)
rem Verify the existence of all used directories by creating them
rem independent on existing already or not and next verifying if
rem the directory really exists finally.
for %%I in ("%Destination%\%NAME%" "%Destination%\%NAME2%") do (
md %%I 2>nul
if not exist "%%~I\" (
echo Error detected by %~f0:
echo/
echo Directory %%I
echo does not exist and could not be created.
echo/
exit /B 3
)
)
rem Remove directories independent on their existence and verify
rem if the directories really do not exist anymore finally.
for %%I in ("%Destination%\%NAME3%") do (
rd /Q /S %%I 2>nul
if exist "%%~I\" (
echo Error detected by %~f0:
echo/
echo Directory %%I
echo still exists and could not be removed.
echo/
exit /B 2
)
)
cd /D X:\test1 2>nul
if /I not "%CD%" == "X:\test1" (
echo Error detected by %~f0:
echo/
echo Failed to set "X:\test1" as current directory.
echo/
exit /B 1
)
This batch file handles nearly all possible errors which could occur during execution of this batch file. A remaining problem could be caused by an environment variable containing one or more double quotes in its value. The solution would be using delayed expansion.
Linux shell script interpreters have the option -e to exit immediately execution of a script if any command or application returns with a value not equal 0. But Windows command interpreter cmd.exe does not have such an option. The options of cmd.exe can be read on running in a command prompt window cmd /?.
So it is necessary to use in a batch file:
if exist "..." exit /B 1 or goto :EOF
if not exist "..." exit /B 1 or goto :EOF
if errorlevel 1 exit /B 1 or goto :EOF
... || exit /B 1 or ... || goto :EOF
See also the Stack Overflow articles:
Windows batch files: .bat vs .cmd?
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?

Windows batch file return code isn't set if there's a following line

The following Windows batch file called "foo.bat" echos "quitting" and sets the return code to 1 as I expect:
if "1"=="1" (
if "1"=="1" (
echo quitting
exit /B 1
)
)
But, to my surprise, the return code of this batch file is 0:
if "1"=="1" (
if "1"=="1" (
echo quitting
exit /B 1
)
echo anything
)
I determine the batch file's return code like so in the Windows command prompt:
> cmd.exe /c foo.bat
quitting
> echo %ERRORLEVEL%
0
I've verified that ERRORLEVEL is not already set in my environment, running set ERRORLEVEL prints "Environment variable ERRORLEVEL not defined" as expected.
Everything else about the second file works as expected. It does echo "quitting" and does not echo "anything". It appears that adding the echo anything line to the script means that the line exit /B 1 still exits but does not set the return code.
This is Windows 7 in EC2. ver reports "Microsoft Windows [Version 6.1.7601]".
Is there a way to ensure exit /B 1 really sets the return code, even in complex if statements?
If I change the file extension from ".bat" to ".cmd", then it behaves as expected.
What is your test environment?
Are you shure therE are no other side effects involved?
This batch got the expected results in Win7Ult and Win10pro
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
ver > nul
Call :First
Echo called First ErrorLevel = %Errorlevel%
ver > nul
Call :Second
Echo called Second ErrorLevel = %Errorlevel%
Pause
Goto:Eof
:First
if "1"=="1" (
if "1"=="1" (
echo quitting
exit /B 1
)
)
Goto :EoF
:Second
if "1"=="1" (
if "1"=="1" (
echo quitting
exit /B 1
)
echo anything
)
quitting
called First ErrorLevel = 1
quitting
called Second ErrorLevel = 1
I had the same trouble. I solved it with something like this:
if "1"=="1" (
if "1"=="1" (
echo quitting
goto Exit1
)
echo anything
)
goto :Eof
:Exit1
exit /b 1
Another "known" defect is this:
rem t.bat
copy non-existing somewhere
rem
C:\> cmd /c t.bat
The system cannot find the file specified.
C:\> echo %errorlevel%
0
Explicit returning of errorlevel at end of batch file does not work when run with cmd /c if there is a comment after (or echo or something that is supposed to not normally change the errorlevel). So need to use this instead:
rem t.bat
copy non-existing somewhere
exit /b %errorlevel%
rem
C:\> cmd /c t.bat
The system cannot find the file specified.
C:\> echo %errorlevel%
1

Resources