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.
Related
I have a simple function written to check for directories:
:direxist
if not exist %~1 (
echo %~1 could not be found, check to make sure your location is correct.
goto:end
) else (
echo %~1 is a real directory
goto:eof
)
:end is written as
:end
endlocal
I don't understand why the program would not stop after goto:end has been called. I have another function that uses the same method to stop the program and it work fine.
:PRINT_USAGE
echo Usage:
echo ------
echo <file usage information>
goto:end
In this instance, the program is stopped after calling :end; why would this not work in :direxist? Thank you for your help!
I suppose you are mixing call and goto statements here.
A label in a batch file can be used with a call or a goto, but the behaviour is different.
If you call such a function it will return when the function reached the end of the file or an explicit exit /b or goto :eof (like your goto :end).
Therefore you can't cancel your batch if you use a label as a function.
However, goto to a label, will not return to the caller.
Using a synatx error:
But there is also a way to exit the batch from a function.
You can create a syntax error, this forces the batch to stop.
But it has the side effect, that the local (setlocal) variables will not be removed.
#echo off
call :label hello
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" goto :halt
exit /b
:halt
call :haltHelper 2> nul
:haltHelper
()
exit /b
Using CTRL-C:
Creating an errorcode similar to the CTRL-C errorcode stops also the batch processing.
After the exit, the setlocal state is clean!
See #dbenham's answer Exit batch script from inside a function
Using advanced exception handling:
This is the most powerful solutions, as it's able to remove an arbitrary amount of stack levels, it can be used to exit only the current batch file and also to show the stack trace.
It uses the fact, that (goto), without arguments, removes one element from the stack.
See Does Windows batch support exception handling?
jeb's solution works great. But it may not be appropriate in all circumstances. It has 2 potential drawbacks:
1) The syntax error will halt all batch processing. So if a batch script called your script, and your script is halted with the syntax error, then control is not returned to the caller. That might be bad.
2) Normally there is an implicit ENDLOCAL for every SETLOCAL when batch processing terminates. But the fatal syntax error terminates batch processing without the implicit ENDLOCAL! This can have nasty consequences :-( See my DosTips post SETLOCAL continues after batch termination! for more information.
Update 2015-03-20 See https://stackoverflow.com/a/25474648/1012053 for a clean way to immediately terminate all batch processing.
The other way to halt a batch file within a function is to use the EXIT command, which will exit the command shell entirely. But a little creative use of CMD can make it useful for solving the problem.
#echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
exit /b
:main
call :label hello
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" exit
exit /b
I've got both my version named "daveExit.bat" and jeb's version named "jebExit.bat" on my PC.
I then test them using this batch script
#echo off
echo before calling %1
call %1
echo returned from %1
And here are the results
>test jebExit
before calling jebExit
hello
stop
>test daveExit
before calling daveExit
hello
stop
returned from daveExit
>
One potential disadvantage of the EXIT solution is that changes to the environment are not preserved. That can be partially solved by writing the environent to a temporary file before exiting, and then reading it back in.
#echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
for /f "eol== delims=" %%A in (env.tmp) do set %%A
del env.tmp
exit /b
:main
call :label hello
set junk=saved
call :label stop
echo Never returns
exit /b
:label
echo %1
if "%1"=="stop" goto :saveEnvAndExit
exit /b
:saveEnvAndExit
set >env.tmp
exit
But variables with newline character (0x0A) in the value will not be preserved properly.
If you use exit /b X to exit from the function then it will set ERRORLEVEL to the value of X. You can then use the || conditional processing symbol to execute a command if ERRORLEVEL is non zero.
#echo off
setlocal
call :myfunction PASS || goto :eof
call :myfunction FAIL || goto :eof
echo Execution never gets here
goto :eof
:myfunction
if "%1"=="FAIL" (
echo myfunction: got a FAIL. Will exit.
exit /b 1
)
echo myfunction: Everything is good.
exit /b 0
Output from this script is:
myfunction: Everything is good.
myfunction: got a FAIL. Will exit.
Here's my solution that will support nested routines if all are checked for errorlevel
I add the test for errolevel at all my calls (internal or external)
#echo off
call :error message&if errorlevel 1 exit /b %errorlevel%<
#echo continuing
exit /b 0
:error
#echo in %0
#echo message: %1
set yes=
set /p yes=[no]^|yes to continue
if /i "%yes%" == "yes" exit /b 0
exit /b 1
I am trying the following:
start /wait /B "C:\Users\Kiriti_Komaragiri\Desktop\sample" npm i
echo Y
start /wait /B "C:\Users\Kiriti_Komaragiri\Desktop\sample2" npm i
I would like to run the above in the same window with auto response "Y"
Currently, its running only the first command and not the third one. I am not sure why?
Here you go.
echo Y | start /wait /B "C:\Users\Kiriti_Komaragiri\Desktop\sample" npm i
echo Y | start /wait /B "C:\Users\Kiriti_Komaragiri\Desktop\sample2" npm i
More information please. What type of command are you trying to feed the response to? Is it Choice, Set /p, or something else your trying to feed a response to?
Without knowing more, the only suggestion I can make requires the existence of a label before the input is processed in the secondary batch.
A workaround exists whereby you can call and arrive at a label in another Batch by calling a label with the same name in your calling Batch. This allows you to define the value of the input (Whatever form the input takes) in your primary batch, then do as follows (substituting label names, variable names and file paths as appropriate)
-In the Calling (Primary) Batch:
Set ResponseVarName=Y
Call :targetLabel
(whatever code your batch has in between)
REM this is where you make your hack 'Call' to the other batch, without actually 'Calling' the batch itself.
:targetLabel
%userprofile%\desktop\yourotherbatch.bat
exit /b
Just to be sure your absolutely clear, this workaround is utterly dependant on being sent to a specific label after the response is set, BEFORE other commands are executed.
(EDIT-) A couple of example programs to show the concept:
::::::::: %userprofile%\desktop\HomeBatch.bat ::::::::::::::::
#ECHO OFF
:main
Set TestEnvironment=1
Call :targetLabel
:nottarget
ECHO NOT target
pause
exit
:targetLabel
%userprofile%\desktop\OtherBatch.bat npm i
:homeBatch
ECHO returned Home
pause
GOTO main
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::: %userprofile%\desktop\OtherBatch.bat ::::::::::::::::
#ECHO OFF
:NottheTarget
ECHO NOT THE TARGET
pause
exit
:targetLabel
ECHO Found the Target. TestEnvironment=%TestEnvironment% %~1 %~2
pause
Exit /b
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
I'm doing a project to help myself learn batch a little better. I have code which is meant to be a "UI" of sorts.
The snippet I have an issue with is this:
:install_check
if exist UI (
echo UI is already installed; reinstall?
CHOICE
if Errorlevel 1 goto :del
if Errorlevel 2 goto :return
cls && goto :install_check
)
goto :install_yes
I know the naming conventions and some of the coding stuff isn't perfect, but I really see no reason why every time it runs it goes to :del no matter what I type in for choice.
I've changed the choice switches and moved it around to be all in one line but it still doesn't want to work. The if that it is nested in is meant to check if a file is present on the computer, so it is a requirement but also may be the issue?
I tried looking around on this site but nothing will help my issue, any help is gladly accepted!!
Check the errorlevels in reverse-order.
if errorlevel n
means if errorlevel is n or greater
hence, errorlevel 2 will be interpreted as true for if errorlevel 1
As a solution has already been determined, the following example, (using your existing label names), restructures your code a little:
:install_check
ClS
If Not Exist "UI" GoTo install_yes
Choice /M "UI is already installed; reinstall"
If Errorlevel 2 GoTo return
GoTo del
If the next line/label in your code is :del, you may also remove line six.If the name UI belonged to a directory instead of a file then you'd change the third line to If Not Exist "UI\" GoTo install_yes.
ERRORLEVEL doesn't update inside control blocks like IF statements unless you use !ERRORLEVEL! instead of %ERRORLEVEL% and use this command at the start of your code: setlocal ENABLEDELAYEDEXPANSION
see http://batcheero.blogspot.ca/2007/06/how-to-enabledelayedexpansion.html
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
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%