How to exit a batch function? - batch-file

So I wanted to do something like this:
#echo off
echo Before
call :test
echo After
pause
:test
echo I'm in test!
:end
echo I shouldn't be here...
Such that it would call test after saying Before and before saying After. However, it keeps just saying I shouldn't be here before saying After! Now, it's not necessarily a problem in this example, but when I have a whole bunch of functions, it can be quite an issue. Can anyone please help me? Thanks! :)

Batch has no concept of "sections", "functions", "procedures" or "paragraphs". A label is simply a reference point. Execution does not stop when a label is reached, it simply continues through, line by line, until it reaches end-of-file, a CALL , a GOTO or an EXIT. This is why you get the message you mention - and why the I'm in test! and the "rogue" message appear after your pause (which you wouldn't see if you are executing the batch by "click"ing it)
You need to goto :eof after completing the mainline, otherwise execution will continue through the subroutine code. :EOF is a predefined label understood by CMD to mean end of file. The colon is required.
You can use goto :eof to terminate a subroutine (reaching end-of-file terminates the routine and returns to the statement after the call) OR you can use exit or exit /b [n] as explained in the documentation (se exit /? from the prompt.
The downside of using exit is that it will terminate the cmd instance if executed when the routine hasn't been called - ie. it's part of the main routine - consequently, goto :eof is more popular.

Related

batch file to rename folders goto command loop the rest of the code [duplicate]

I'm trying to understand where in the code exactly does GOTO :EOF return to?
Here is the code:
SET count=1
FOR /f "tokens=*" %%G IN (somefile.txt) DO (call :subroutine "%%G")
GOTO :EOF
:subroutine
echo %count%:%1
set /a count+=1
GOTO :EOF
:EOF is a predefined label as Microsoft explains in documentation for command GOTO. The help output by running in a command prompt window goto /? explains also this special label for End Of File. But this predefined label is supported only with command extensions being enabled as by default.
The help output by running in a command prompt window call /? and of course also the documentation for command CALL explain both that goto :EOF should be used to exit a subroutine called with call :Label.
A subroutine is nothing else than another batch file embedded within current batch file called with command call. If the subroutine is at end of the batch file, real end of file marks the end of the subroutine.
But there can be multiple subroutines in a batch file.
So a command is needed for command interpreter to exit the subroutine on reaching a specific line in command processing and go back to the calling command line. goto :EOF as well as exit /B can be both used everywhere to either exit a subroutine or exit the current batch file processing.
In batch code in question the first goto :EOF is needed to exit batch file processing without an unwanted fall through to the subroutine code after finishing the loop.
The second goto :EOF in batch code of questioner is for exiting the subroutine and continue processing in FOR loop in second line. It does not exit processing of the batch file, it exits only the processing of the subroutine.
Note 1: goto EOF without a colon requires that there is really a line starting with :EOF in the batch file, i.e. the label EOF must exist in the file. goto :EOF always results in exiting subroutine/batch processing with command extensions enabled even if there is a label EOF in the batch file because of a line starting with :EOF.
Note 2: Command EXIT without parameter /B results always in exiting entire command process independent on calling hierarchy and independent on how the Windows command processor was started – with parameter /K to keep cmd.exe running as used when opening a command prompt window or with /C to close after command processing finished as used on double clicking a batch file. Therefore exit without /B should be used wisely in a batch file (best: never).
Note 3: exit /B without or with an exit code works always, but outputs an error message with command extensions disabled as demonstrated by this code:
#echo off
setlocal DisableExtensions
echo Use command exit /B with command extensions disabled.
exit /B 5
echo This line is not processed anymore.
Executing this batch file from within a command prompt window results in output of the error message:
The system cannot find the batch label specified - EOF
But the processing of the batch file is exited nevertheless with the exit code value 5 as it can be seen on running next in same command prompt window echo ERRORLEVEL is: %ERRORLEVEL% which outputs: ERRORLEVEL is: 5
It looks like there is assigned first the specified exit code value 5 to the dynamic variable ERRORLEVEL on using exit /B 5 and next is executed goto :EOF because of option /B. That fails because of disabled command extensions resulting in the error message and in exiting the batch file processing as it always occurs on a label to go to does not exist in a batch file.
In other words exit /B without or with an additional exit code always works, but there should be appended 2>nul to suppress the error message on command extensions disabled, i.e. use exit /B 2>nul (without exit code) or exit /B 5 2>nul (with exit code)
Note 4: ERRORLEVEL is not affected by goto :EOF, but the Microsoft GOTO documentation is mute on this topic. exit /B # sets ERRORLEVEL to # as documented by Microsoft. exit /B # can be also used instead of goto :EOF to exit a subroutine with a specific exit code evaluated on the command line calling the subroutine like on using the operators && or || or on next command after calling command line with if errorlevel X. However, explicitly exiting a batch file or a subroutine with a specific exit code is usually not needed as neither goto :EOF nor exit /B modify the current value of ERRORLEVEL.
Note 5: Do not use goto:EOF or call:Label in a batch file with no space between command GOTO respectively CALL (argument 0) and the label (argument 1). There should be always used goto :EOF and call :Label with a space as argument strings separator between command and label. The reason is that goto:EOF results in the attempts to find in current directory first a file with name goto: and next a file with name goto:EOF. The incorrect command call:Label results in searching for a file with name call: and next with name call:Label. The file system returns for both syntactically wrong commands twice to cmd.exe that the name is invalid. Then cmd.exe detects the colon as reason for the invalid name and splits the command up into command and label argument and finally runs the command with success. The usage of goto :EOF and call :Label does not cause any wrong file system accesses as cmd.exe immediately recognizes the string goto respectively call as internal command.
For details on ERRORLEVEL behavior see:
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Single line with multiple commands using Windows batch file
GOTO :EOF is functionally equivalent to exit /B, but both forms only works when Extensions are enabled. The test for this point is very simple:
setlocal DisableExtensions
goto :EOF
Compare previous code vs. this one:
setlocal DisableExtensions
exit /B
This means that GOTO :EOF returns to the same point where exit /B would return.
:eof means "End of file". It's used to make the script finish without executing any of the commands below.
As the GOTO and CALL are using the same functionality to find a label you have an option to access the :EOF with CALL too:
CALL ::EOF
Same as GOTO (when CALL with the additional semicolon) the actual end-of-file will be the preferable point where the flow of the script will go. If you have your own label\function defined with EOF you can access it with single semicolon.
Though calling the :EOF has no much use - you can't put a code after the end of file so this line is actually doing nothing (though this will hit the performance as the end of file is parsed). And as GOTO and EXIT /B this wont work without enabled extensions.

How to get a batch file only processed if it's called from another batch file?

I am coding a batch file and it needs some more files. But they files should only be able to run using the call function from another batch file. My code looks like this:
call compileData.bat
pause
I want the compilerData.bat just starts when it's called from this one, not if its just started from Explorer or something other.
Can you please help me?
I have tried to find a solution on this problem in a whole hour!
You can use a parameter.
compileData.bat:
if "%1" neq "somestring" exit /b
REM rest of your code
Another.bat:
call compileData.bat somestring
pause
I cannot think of any way that would prevent the bare "run" of the called script. Possibly that might only be done using NTFS permissions.
What you can do quickly is something like this:
MOTHERBATCH.bat
call compileData.bat SomePASSPHRASE
compileData.bat
#echo off
if not "%1"=="SomePASSPHRASE" (
echo "You can not run this script directly, please run MOTHERSCRIPT.bat."
exit /B 1
)
echo "Passphrase is correct, code is executed..."
Set an environment variable in the parent script, then if that variable is not set or doesn't have the correct value in the children, they just exit with an error message explaining they aren't intended for standalone use. You really can't prevent someone from reverse engineering the code and forcing it to run.
You could put the children in a password protected zip file and have the parent unpack it just before calling them. Then when the parent is done, it deletes the unpacked scripts.
Do all of the above.
You can use a not so well known system variable named cmdcmdline.
I will explain a brief usage for you.
For brevity's sake we will have two very simple batch files.
Parent.bat
#echo off
call compiledata.bat
And compiledata.bat
#echo off
echo %cmdcmdline%
pause
When compiledata.bat is executed on its own this variable's value is the batch file itself.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\compiledata.bat" "
But when compiledata.bat is called from parent.bat the variable's value is that of the calling parent.bat.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\parent.bat" "
My suggestion is putting all your batch code into a single batch file and use subroutines. Open a command prompt window and run call /? for help on how to use subroutines which is nothing else than calling a batch file being embedded in current batch file.
A simple example:
#echo off
echo Running %~f0 %*
call :compileData %*
call :WaitForUser
rem The next line results in exiting processing of this batch file
goto :EOF
:compileData
echo/
echo Running subroutine compileData with the arguments: %*
rem Exit processing subroutine compileData and continue above
rem after the command line calling the subroutine compileData.
goto :EOF
:WaitForUser
echo/
pause
rem Exit processing subroutine WaitForUser and continue above
rem after the command line calling the subroutine WaitForUser.
goto :EOF
See also Where does GOTO :EOF return to? And take a look on DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the explanation on using echo/ to output an empty line.
Here's my solution:
when launched from the command line, %cmdcmdline% inherits the name from the base calling program, so it wouldn't be the name of the "middle man" calling your batch file
this is what I came up with. I had to use the "subroutine" method to get the variables properly expanded
Note: Edge Case: if you use complex paths with the batch files having the same name in different folders, you could run into an "Edge Case". If that is important to you, then you might have to further parse the file names. I'm not totally sure, it wasn't my use case so I didn't go further.
#echo OFF
setlocal EnableDelayedExpansion
call :myGetFileName "%CmdCmdLine%"
if /I "%sRet%"=="%~nx0" (
echo ************** Pause
) else (
echo ************** NO Pause
)
echo finished test
pause
exit
:myGetFileName
set "sRet=%~nx1"
exit /b

Where does GOTO :EOF return to?

I'm trying to understand where in the code exactly does GOTO :EOF return to?
Here is the code:
SET count=1
FOR /f "tokens=*" %%G IN (somefile.txt) DO (call :subroutine "%%G")
GOTO :EOF
:subroutine
echo %count%:%1
set /a count+=1
GOTO :EOF
:EOF is a predefined label as Microsoft explains in documentation for command GOTO. The help output by running in a command prompt window goto /? explains also this special label for End Of File. But this predefined label is supported only with command extensions being enabled as by default.
The help output by running in a command prompt window call /? and of course also the documentation for command CALL explain both that goto :EOF should be used to exit a subroutine called with call :Label.
A subroutine is nothing else than another batch file embedded within current batch file called with command call. If the subroutine is at end of the batch file, real end of file marks the end of the subroutine.
But there can be multiple subroutines in a batch file.
So a command is needed for command interpreter to exit the subroutine on reaching a specific line in command processing and go back to the calling command line. goto :EOF as well as exit /B can be both used everywhere to either exit a subroutine or exit the current batch file processing.
In batch code in question the first goto :EOF is needed to exit batch file processing without an unwanted fall through to the subroutine code after finishing the loop.
The second goto :EOF in batch code of questioner is for exiting the subroutine and continue processing in FOR loop in second line. It does not exit processing of the batch file, it exits only the processing of the subroutine.
Note 1: goto EOF without a colon requires that there is really a line starting with :EOF in the batch file, i.e. the label EOF must exist in the file. goto :EOF always results in exiting subroutine/batch processing with command extensions enabled even if there is a label EOF in the batch file because of a line starting with :EOF.
Note 2: Command EXIT without parameter /B results always in exiting entire command process independent on calling hierarchy and independent on how the Windows command processor was started – with parameter /K to keep cmd.exe running as used when opening a command prompt window or with /C to close after command processing finished as used on double clicking a batch file. Therefore exit without /B should be used wisely in a batch file (best: never).
Note 3: exit /B without or with an exit code works always, but outputs an error message with command extensions disabled as demonstrated by this code:
#echo off
setlocal DisableExtensions
echo Use command exit /B with command extensions disabled.
exit /B 5
echo This line is not processed anymore.
Executing this batch file from within a command prompt window results in output of the error message:
The system cannot find the batch label specified - EOF
But the processing of the batch file is exited nevertheless with the exit code value 5 as it can be seen on running next in same command prompt window echo ERRORLEVEL is: %ERRORLEVEL% which outputs: ERRORLEVEL is: 5
It looks like there is assigned first the specified exit code value 5 to the dynamic variable ERRORLEVEL on using exit /B 5 and next is executed goto :EOF because of option /B. That fails because of disabled command extensions resulting in the error message and in exiting the batch file processing as it always occurs on a label to go to does not exist in a batch file.
In other words exit /B without or with an additional exit code always works, but there should be appended 2>nul to suppress the error message on command extensions disabled, i.e. use exit /B 2>nul (without exit code) or exit /B 5 2>nul (with exit code)
Note 4: ERRORLEVEL is not affected by goto :EOF, but the Microsoft GOTO documentation is mute on this topic. exit /B # sets ERRORLEVEL to # as documented by Microsoft. exit /B # can be also used instead of goto :EOF to exit a subroutine with a specific exit code evaluated on the command line calling the subroutine like on using the operators && or || or on next command after calling command line with if errorlevel X. However, explicitly exiting a batch file or a subroutine with a specific exit code is usually not needed as neither goto :EOF nor exit /B modify the current value of ERRORLEVEL.
Note 5: Do not use goto:EOF or call:Label in a batch file with no space between command GOTO respectively CALL (argument 0) and the label (argument 1). There should be always used goto :EOF and call :Label with a space as argument strings separator between command and label. The reason is that goto:EOF results in the attempts to find in current directory first a file with name goto: and next a file with name goto:EOF. The incorrect command call:Label results in searching for a file with name call: and next with name call:Label. The file system returns for both syntactically wrong commands twice to cmd.exe that the name is invalid. Then cmd.exe detects the colon as reason for the invalid name and splits the command up into command and label argument and finally runs the command with success. The usage of goto :EOF and call :Label does not cause any wrong file system accesses as cmd.exe immediately recognizes the string goto respectively call as internal command.
For details on ERRORLEVEL behavior see:
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Single line with multiple commands using Windows batch file
GOTO :EOF is functionally equivalent to exit /B, but both forms only works when Extensions are enabled. The test for this point is very simple:
setlocal DisableExtensions
goto :EOF
Compare previous code vs. this one:
setlocal DisableExtensions
exit /B
This means that GOTO :EOF returns to the same point where exit /B would return.
:eof means "End of file". It's used to make the script finish without executing any of the commands below.
As the GOTO and CALL are using the same functionality to find a label you have an option to access the :EOF with CALL too:
CALL ::EOF
Same as GOTO (when CALL with the additional semicolon) the actual end-of-file will be the preferable point where the flow of the script will go. If you have your own label\function defined with EOF you can access it with single semicolon.
Though calling the :EOF has no much use - you can't put a code after the end of file so this line is actually doing nothing (though this will hit the performance as the end of file is parsed). And as GOTO and EXIT /B this wont work without enabled extensions.

How Can I Make A Command Prompt Hang?

Yes, I know you are probably going to complain saying it's a bad thing to do, but I want to do it anyway!
I am creating a batch program and at the end, I need it to hang and not accept user input. I know one method is just creating an infinite loop of:
:pause
pause > nul
goto pause
but I don't think that's a great choice. Although I need it to hang, I need to to be able to be closed via the red 'X' close button at the top of the window.
Any ideas?
This works for me. It redirects < NUL into self to prevent Ctrl+C from breaking, and uses start /b /wait to suppress the "Terminate batch job (Y/N)?" prompts.
#echo off
setlocal
>NUL (echo(%* | findstr "\<hang\>" && waitfor redX)
rem // *** PUT YOUR MAIN SCRIPT HERE ***
echo End of line.
rem // ******* END MAIN SCRIPT *********
call :hang
goto :EOF
:hang
start /b /wait "" "%~f0" hang ^<NUL
On the initial launch of the script, the echo(%* | findstr "\<hang\>" >NUL line looks for a script argument of "hang". If found, the script executes the waitfor command.
Normally, waitfor can be broken with Ctrl+C. But since the usual behavior of Ctrl+C is defeated by start /b and <NUL, the hanging effect is achieved unless a user does Ctrl+Break or sends the answering waitfor signal.
The red X still works, though.

Windows Batch: can't exit from called subroutine- always returns even if goto:END

I have a subroutines which is called to check ERRORLEVEL.
The subroutine calls other subroutines to log msgs, send email, then Exit out of the script. It goes to :END, then returns to the stmt after the call
#echo off
echo starting...
call:checkTime
echo +++ after CT
GOTO:END
:checkTime
echo the time is %TIME%
goto:END
goto:EOF
:END
The question is poorly worded, but I think I understand (especially if I concentrate on the title)
My interpretation of your problem:
At various points in your batch file, you check the ERRORLEVEL. Whenever you detect an error, you want to perform some standard error processing, and then exit the batch script. You attempted to create a subroutine to do the standard processing, but the subroutine returns to the caller instead of exiting the script. Your question is, how do you force your error processing routine to exit instead of returning to caller?
Answer:
If none of your error detection occurs within a called subroutine, then you can simply GOTO your error processor instead of CALLing it.
If you want to be able to call the routine and exit from within another called routine, then you can continue to use the CALL statement, but terminate your error routine with EXIT instead of GOTO :EOF or GOTO :END.
Addendum in response to comment
Yes, GOTO cannot pass parameters, and a CALLed routine will always return to the caller (unless the routine ends with EXIT)
And yes, EXIT will close the current CMD shell, which will usually close the console window.
BUT... you can have the batch file execute itself through another CMD shell, so that EXIT does not close the window!
The only potential drawback to this that I see is changes to the environment will be lost once the batch file (and the CMD shell that is running it) terminates. That may or may not be a problem for you.
#echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
exit /b
:main
shift /1
echo %%1=%1 %%2=%2
echo before call
call :exitRoutine
:: should not get here
echo after call
exit /b
:exitRoutine
echo exiting batch file witin exitRoutine
exit
Yes, this is the expected behavior: a subroutine called via CALL command may end in three different ways: executing EXIT [/B], executing GOTO :EOF or just reaching the end of the file. All three ways cause to return to the calling program. By the while, "GOTO command now accepts a target label of :EOF which transfers control to the end of the current batch script file. This is an easy way to exit a batch script file without defining a label." (from GOTO /?), so really the second and third methods are the same.
If you want to sometimes return from the subroutine, but other times to terminate the calling program, then your subprogram can NOT be executed via CALL, but in other different way. If you want to pass parameters to the subprogram, then it must be a separate .BAT file that will be executed via its name with parameters and NO call, for example:
subprogram param1 param2 ...
This way, in order for this subprogram to "return" to the calling program, it must know to wich Batch file and in what line it must return. This information may be set by the calling program via variables; the calling program must also determine if it is running in normal way or because the return of the false subroutine. You may do that this way:
main.bat:
#echo off
rem If this is a false subroutine return: complete it
if "%1" == "return" goto %2
rem Do my usual business
rem . . .
rem Execute the subprogram as subroutine
set caller=main
set returnPoint=label23
subprogram param1 param2
:label23
rem Continue after subprogram return...
subprogram.bat:
rem Do my business
rem . . .
rem To return to the caller:
%caller% return %returnPoint%
rem . . .
rem To terminate here, execute EXIT /B, GOTO :EOF, or just reach the end
Sorry, there is no easy way to do this...

Resources