In bash "set -e" at the beginning of the script instructs bash to fail the whole script on first failure of any command inside.
How do I do the same for a Windows batch script?
Tuim's solution works, but it can be made even simpler.
The ERRORLEVEL is already set, so there is no need to GOTO a label that sets the ERRORLEVEL.
You can simply use
yourCommand || exit /b
Note that exit /b will only exit the current subroutine if you are in the middle of a CALL. Your script will have to exit each CALL, layer by layer, until it reaches the root of the script. That will happen automatically as long as you also put the test after each CALL statement
call :label || exit /b
It is possible to force a batch script to exit immediately from any CALL depth. See How can I exit a batch file from within a function? for more info. Be sure to read both answers. The accepted answer has a couple of potentially serious drawbacks.
Not directly but you can add the following to every line which has something to execute.
|| goto :error
And then define error, which stops the script.
:error
exit /b %errorlevel%
Related
I would like to ask you if you could hint me to a somehow more elegant solution (than the one provided at the end of this post) to this problem: I want to run a batch script which, if (at least) one of its instructions fails, will continue running the other instructions, but will return an errorlevel indicating a failure (say, 1).
One solution which I tested (and works) is the following:
set instructionfailed=0
**INSTRUCTION THAT WILL FAIL (ex. copy a a)**
if %errorlevel% == 1 set instructionfailed=1
**INSTRUCTION THAT WILL SUCCEED (ex.(supposing abcdefg does not exist) mkdir abcdefg) **
if %errorlevel% == 1 set instructionfailed=1
exit /b %instructionfailed%
EDIT: I have actually read (here, for example) that maybe I should use ERRORLEVEL instead of %errorcode%, but the code I have to integrate this with also seems to use %errorcode%.
There is one exceptionally simple technique that may work, depending on which commands are executed, and another slightly more complex technique that will always work.
1) Simple solution that may or may not work, depending on the commands
Some internal commands only set the ERRORLEVEL if and only if there was an error, and some always set the ERRORLEVEL upon success or error. External commands always set the ERRORLEVEL.
So if all of your commands are internal commands that do not clear the ERRORLEVEL upon success, then you can simply clear the ERRORLEVEL at the start, and then run each command in succession. The ERRORLEVEL will only be non-zero (indicating an error) if any one of the commands failed. You can simply issue EXIT /B at the end, and the current ERRORLEVEL will be returned.
An arcane but quick way to clear the ERRORLEVEL to 0 at the start is to use (call ) - the trailing space is critical.
(call )
internalCommand1
internalCommand2
...
internalCommandN
exit /b
A list of internal commands that can work with this solution may be found at Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?. Again, for this solution to work, you want the internal commands that do not clear the ERRORLEVEL upon success.
2) Slightly more complex solution that always works
Just as with your current solution, you set your own error variable to 0 at the start, and conditionally set it to non zero upon error after each command. But instead of using if errorlevel 1 or if %errorlevel% neq 0, you can use the conditional || command concatenation operator, which only executes the command if the preceding one failed.
set "err=0"
anyCommand1 || set "err=1"
anyCommand2 || set "err=1"
...
anyCommandN || set "err=1"
exit /b %err%
I am writing a batch script to extract data from a server. If any command fails in the script below, I want to exit the batch program and record the error in an error log file.
Here is what I have so far:
call epmautomate login aaaa#a.com abcd123 https://www.website.com
epmautomate exportdata ABC_DATA_EXTRACT & epmautomate downloadfile ABC_DATA_EXTRACT.zip & MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" C:\Users\Administrator\Documents\folder & cd C:\Users\Administrator\Documents\folder & unzip -n C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip & C:\Users\Administrator\Documents\folder\change_header.cmd
how can I add error detection and logging?
Before I discuss the logging and the errors I would like to point out what the reason was we wanted you to clean your code (you didn't do it as how we suggested):
there is no reason why you should use the & between your commands. Put each command on a separate line. & means "whatever happens execute also the following" but makes scripts unreadable. So if possible always put each command on a seperate line instead.
you use different ways to execute epmautomate. If it is a script use call each time you want to execute it else don't use call at all when you wish to execute epmautomate. Supposing your code works, I can assume it is not a script and the call isn't needed.
it is preferable to surround all your paths with double qoutes
Now the logging and error detection.
I know 2 different approaches for logging errors and making a batch-file exit on an error. The most important condition is that all commands (either personnal, built-in or 3rd party scripts/software) you use in your script must set the errorlevel correctly. That is the only condition if you want the cmd interpreter to know that an error occured during the execution of a command. Another condition this time for a proper log-file is that the commands you use should write proper error messages. In both approaches the error messages written to the error channel are appended to the log-file using the 2>> error redirection operator (the link also shows how to redirect both output and error messages if interested). I'll assume the path to the log-file without surrounding double quotes is available in the logfile variable. I've added the double quotes each time the variable is used: "%logfile%".
The first approach makes use of an IF statement. The IF ERRORLEVEL n will check if the errorlevel is greater or equal to n. So if we assume a command command1 sets errorlevels correctly (0 if successful, 1 or greater otherwise), the following should be able to stop your script if an errorlevel occured during its execution
command1 2>> "%logfile%"
IF ERRORLEVEL 1 (
REM some extra personal errormessage if needed
echo command1 failed, check the log-file for more info
REM Exit current script and set errorlevel on 1 (failure)
exit /b 1
)
You can exit with another strict positive integer if you want and use personal error codes.
The second approach makes use of conditional execution execution inside code blocks (groups of commands). command1 && command2 will execute command2 only if command1 was succesful. The errorlevel after command1 && command2 will reveal if both exited successfully or if one of them exited with an error. If you group commands together between ( ) like this:
(
command1
command2
command3
)
the cmd interpreter will just put all commands on one line and put && in between: it will end up executing command1 && command2 && command3. So to execute a group of commands and exit if an error occured during execution of one of the commands, one can use
(
command1
command2
command3
) 2>> "%logfile%"
IF ERRORLEVEL 1 (
REM some extra personal errormessage if needed
echo An error occured, consult the log-file for more info
REM Exit current script and set errorlevel on 1 (failure)
exit /b 1
)
There is one downside for this approach: the use of variables inside the code blocks is limited. As all commands inside the block are parsed and executed as one single command, you cannot give a variable a new value inside the block and use that new value inside the same block with the normal variable expansion (i.e. %var%). You'll have to use delayed expansion if you want to be able to use the new value.
Which approach you pick depends on the situation and on what you want to achieve. The first approach is a more general one. Thanks to the "high" variety of IF statements, it can be used for commands that don't set the errorlevel but use another way of communicating an error that occurred during execution. The first approach also allows a more accurate error analysis because you know which command caused the error and can add that info in the log-file easily. There is a problem though: you'll have some serious type work. You can try to solve that issue by using a function that executes all your commands and exit the batch script from within the function if needed but it's not that easy. I have another more easy option to abandon the script when using the function but I'll come back to it later.
The second approach has the disadvantage that you can't easily identify the command where the error occurred. You can solve that issue by printing a "success" message after each command to the log-file. After all, a good log-file should also contain what has been executed successfully.
#echo off
set logfile=C:\Users\path\to\logfile errors.txt
(
epmautomate login aaaa#a.com abcd123 https://www.website.com 2>> "%logfile%"
echo first epmautomate ok >> "%logfile%"
epmautomate exportdata ABC_DATA_EXTRACT 2>> "%logfile%"
echo second epmautomate ok >> "%logfile%"
epmautomate downloadfile ABC_DATA_EXTRACT.zip 2>> "%logfile%"
echo third epmautomate ok >> "%logfile%"
MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" "C:\Users\Administrator\Documents\folder" 2>> "%logfile%"
echo move zip ok >> "%logfile%"
cd "C:\Users\Administrator\Documents\folder" 2>> "%logfile%"
echo cd to admin folder ok >> "%logfile%"
unzip -n "C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip" 2>> "%logfile%"
echo unzip zipfike ok >> "%logfile%"
call "C:\Users\Administrator\Documents\folder\change_header.cmd" 2>> "%logfile%"
echo call to change-header ok >> "%logfile%"
)
IF ERRORLEVEL 1 (
REM some extra personal errormessage if needed
echo An error occured, consult the log-file for more info
REM Exit current script and set errorlevel on 1 (failure)
exit /b 1
)
It does the job. You'll just have to write each thing you want in your log and move the error redirections inside the code block . If an error occurs you'll still have to look back in your code to know which command produced the last error messages in your log-file though but at least you'll know which command failed in your code-block thanks to the success messages.
For the completeness I'll add the solution for the first approach using a function to execute your commands as I said earlier (which I actually prefer). But because exiting a script from a function can be quite complex I would rather use the idea from the second approach (conditional execution inside a code-block) to abandon execution of the rest of the commands:
#echo off
set logfile=C:\Users\path\to\logfile_errors.txt
(
call :executeOwn epmautomate login aaaa#a.com abcd123 https://www.website.com
call :executeOwn epmautomate exportdata ABC_DATA_EXTRACT
call :executeOwn epmautomate downloadfile ABC_DATA_EXTRACT.zip
call :executeOwn MOVE "C:\doc\bin\ABC_DATA_EXTRACT.zip" "C:\Users\Administrator\Documents\folder"
call :executeOwn cd "C:\Users\Administrator\Documents\folder"
call :executeOwn unzip -n "C:\Users\Administrator\Documents\folder\ABC_DATA_EXTRACT.zip"
call :executeOwn call "C:\Users\Administrator\Documents\folder\change_header.cmd"
)
REM Exit current batch script with error status from last executed call
exit /b %ERRORLEVEL%
:executeOwn
%* 2>> "%logfile%"
IF ERRORLEVEL 1 (
REM Write command that was executing to log-file
echo FAILED : [ %* ] >> "%logfile%"
REM some extra personal errormessage if needed
echo An error occured, consult the log-file for more info
REM Exit current script and set errorlevel on 1 (failure)
exit /b 1
)
echo succeeded : [ %* ] >> "%logfile%"
exit /b 0
This method will even allow to solve the problem with the variable expansion on a way that doesn't require delayed expansion. You'll just have to use %%var%% instead of the usual %var% to expand variables inside the code block and the :executeOwn will be able to expand it to its newest value. Beware: special characters like ^&<> inside the code-block will have to be escaped with a caret ^^^&^<^> in order to be executed in the :executeOwn function except if they are part of double quoted string.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
For a batch file I want to check for different conditions and have a help message
Which one is the best practice for exiting the batch file after displaying the message?
if "%1"=="/?"(
echo "help message"
exit /b 0
)
[more code]
or
if "%1"=="/?"(
echo "help message"
goto :EOF
)
[more code]
:EOF
The first one seems better for my untrained eyes but a lot of the examples online use the GOTO tag method
What is the SO community's opinion on this?
Personally I use exit.
The normal exit command simply terminates the current script, and the parent (for example if you were running a script from command line, or calling it from another batch file)
exit /b is used to terminate the current script, but leaves the parent window/script/calling label open.
With exit, you can also add an error level of the exit. For example, exit /b 1 would produce an %errorlevel% of 1. Example:
#echo off
call :getError rem Calling the :getError label
echo Errorlevel: %errorlevel% rem Echoing the errorlevel returned by :getError
pause
:getError
exit /b 1 rem exiting the call and setting the %errorlevel% to 1
Would print:
Errorlevel: 1
press any key to continue...
Setting error levels with this method can be useful when creating batch scripts that may have things that fail. You could create separate :labels for different errors, and have each return a unique error level.
goto :eof ends the current script (call) but not the parent file, (similarly to exit /b)
Unlike exit, in which you can set an exiting errorlevel, goto :eof automatically sets the errorlevel to the currently set level, making it more difficult to identify problems.
The two can also be used in unison in the same batch file:
#echo off
call :getError
echo %errorlevel%
pause
goto :eof
:getError
exit /b 2
Another method of exiting a batch script would be to use cmd /k When used in a stand-alone batch file, cmd /k will return you to regular command prompt.
All in all i would recommend using exit just because you can set an errorlevel, but, it's really up to you.
There is no functional or performance difference between GOTO :EOF vs EXIT /B, except that EXIT /B allows you to specify the returned ERRORLEVEL, and GOTO :EOF does not.
Obviously if you want to specify the ERRORLEVEL when you return, then EXIT /B is preferred.
If you don't care about the return code, or you know that the ERRORLEVEL is already set to the correct value, then it makes no difference - it becomes strictly a matter of preference / coding style.
My question is how would I make a batch script instead of closing when the X in the top right is pressed to execute a file called exit.exe.
There are a couple points in this question that are not clear enough:
If you want that when the rigth top X is pressed on the cmd.exe window it not close, but do a different thing instead, then there is no way to achieve such thing with any existent window, that is, with the windows of all existent applications.
If you want to differentiate if the window that execute your Batch file terminated normally or terminated because the user click on the right top X, then the way to achieve that is via a previous "starter" program that execute your Batch file and expects a certain value returned from it. If the returned value is not the expected one, then it is assumed that the window was cancelled via the right top X.
starter.bat:
#echo off
echo Start yourScript.bat:
start "" /W yourScript.bat
echo Value returned from the script: %errorlevel%
if %errorlevel% neq 12345 ECHO execute exit.exe
yourScript.bat:
#echo off
echo I am the script.bat
set /P var=input value:
rem Terminate normally:
exit 12345
the [X] is "out of reach" for cmd. Only way, I can think of is: create another cmd to watch the presence of the current window:
#echo off
title WatchMe
more +7 %~f0 >t2.bat
start "watcher" t2.bat
exit /b
#echo off
:running
tasklist /v|find "WatchMe" >nul &&echo waiting || goto finished
timeout 1 >nul
goto running
:finished
echo "the process has finished
i try to make a loop in a .cmd file.
If test.txt is not exists then i will kill the cmd process.
#echo off
if not exists test.txt goto exit
But this code doesn't work and i don't know how to make a loop every 2 seconds.
Thanks for help.
The command is called exist, not exists:
if not exist test.txt goto :exit
echo file exists
:exit
About your loop:
I am not 100% sure, but I think there is no sleep or wait command in Windows. You can google for sleep to find some freeware. Another possibility is to use a ping:
ping localhost -n 3 >NUL
EDIT:
The Windows Server 2003 Resource Kit Tools contains a sleep.
See here for more information, too
If you need to wait some seconds use standard CHOICE command. This sample code check if file exist each two seconds. The loop ends if file exists:
#ECHO OFF
:CHECKANDWAITLABEL
IF EXIST myfile.txt GOTO ENDLABEL
choice /C YN /N /T 2 /D Y /M "waiting two seconds..."
GOTO CHECKANDWAITLABEL
:ENDLABEL
exit is a key word in DOS/Command Prompt - that's why goto exit
doesn't work.
Using if not exist "file name" exit dumps you out of that batch file.
That's fine if exiting the batch file is what you want.
If you want to execute some other instructions before you exit, change the label to something like :notfound then you can goto notfound
and execute some other instructions before you exit.
(this is just a clarification to one of the examples)
Using the following:
if not exist "file name" goto exit
Results in:
The system cannot find the batch label specified - exit
However using the same command without "goto" works, as follows:
if not exist "file name" exit