BATCH_ Subroutines in Windows Batch
Since Windows 2000 you can use subroutines (depending on the understanding and definition of "function" and "procedure", one can also refer to them as "procedures", that can manipulate both local and global variables and don't return any value ("void")).
Basically, a subroutine is a number of commands between a label and a GOTO:EOF statement.
GOTO:EOF is often written without a space, as it is used as a special GOTO command: It jumps to the End of File, thus ending the currently executed script.
That is the reason why a subroutine is not called with GOTO :MY_SUBROUTINE, but with
This executes the current batch AGAIN in the current context, but jumps immediately to the given label. And that is the reason why ending the script at the end of the subroutine - it just ends the execution started through the CALL command.
A subroutine has its own parameters, you don't have access to the "parent" batch parameters unless they were stored in an environment variable.
You can see that there is another GOTO:EOF after the CALL command. You don't want your first subroutine being executed after your script finished, do you? This is the basic structure of a batch script using subroutines:
What about the local context I mentioned at the beginning? That does not differ from standard batch scripts:
As subroutines do not provide a way of returning any value (and, of course, there is no direct way to store the result of an execution in a variable), here is what I do. By convention, I reserve the global variable %RESULT% for storing the result of the last called subroutine that should return a value. You can think of it like putting a value onto a one-element stack.
This won't work if using local context within the subroutine, however due to the cmd.exe-nature there is way to preserve a variable after ending the local context:
A little explanation: As the batch reads a full line, expands all variables, parses the line and then finally executes all contained commands in order, the important line looks like this after expansion:
A little advise at the end: document your subroutines, otherwise you won't know what parameters it takes, what global variables it manipulates or requires or what value it "returns". I use double colons for subroutine documentation.
See also this article about handling parameters and variables containing special characters.
Basically, a subroutine is a number of commands between a label and a GOTO:EOF statement.
:MY_SUBROUTINE
ECHO Called MY_SUBROUTINE and ending now...
GOTO:EOF
ECHO Called MY_SUBROUTINE and ending now...
GOTO:EOF
GOTO:EOF is often written without a space, as it is used as a special GOTO command: It jumps to the End of File, thus ending the currently executed script.
That is the reason why a subroutine is not called with GOTO :MY_SUBROUTINE, but with
CALL :MY_SUBROUTINE
This executes the current batch AGAIN in the current context, but jumps immediately to the given label. And that is the reason why ending the script at the end of the subroutine - it just ends the execution started through the CALL command.
A subroutine has its own parameters, you don't have access to the "parent" batch parameters unless they were stored in an environment variable.
CALL :MY_SUBROUTINE "param 1" "param 2"
GOTO:EOF
:MY_SUBROUTINE
ECHO The first parameter is %~1.
ECHO The second parameter is %~2.
GOTO:EOF
GOTO:EOF
:MY_SUBROUTINE
ECHO The first parameter is %~1.
ECHO The second parameter is %~2.
GOTO:EOF
You can see that there is another GOTO:EOF after the CALL command. You don't want your first subroutine being executed after your script finished, do you? This is the basic structure of a batch script using subroutines:
@ECHO off
REM Documentation here
REM Version 1.0 of 2007/11/11
... preprocessing, setting variables etc ...
... script body ....
GOTO:EOF
REM ############################
REM SUBROUTINES
:MY_SUBROUTINE
... subroutine body
GOTO:EOF
REM Documentation here
REM Version 1.0 of 2007/11/11
... preprocessing, setting variables etc ...
... script body ....
GOTO:EOF
REM ############################
REM SUBROUTINES
:MY_SUBROUTINE
... subroutine body
GOTO:EOF
What about the local context I mentioned at the beginning? That does not differ from standard batch scripts:
:MY_SUBROUTINE
SETLOCAL
... local context here ...
ENDLOCAL
GOTO:EOF
SETLOCAL
... local context here ...
ENDLOCAL
GOTO:EOF
As subroutines do not provide a way of returning any value (and, of course, there is no direct way to store the result of an execution in a variable), here is what I do. By convention, I reserve the global variable %RESULT% for storing the result of the last called subroutine that should return a value. You can think of it like putting a value onto a one-element stack.
:MY_SUBROUTINE
SET RESULT=4711
GOTO:EOF
SET RESULT=4711
GOTO:EOF
This won't work if using local context within the subroutine, however due to the cmd.exe-nature there is way to preserve a variable after ending the local context:
:MY_SUBROUTINE
SETLOCAL
SET RESULT=4711
ENDLOCAL & SET RESULT=%RESULT%
GOTO:EOF
SETLOCAL
SET RESULT=4711
ENDLOCAL & SET RESULT=%RESULT%
GOTO:EOF
A little explanation: As the batch reads a full line, expands all variables, parses the line and then finally executes all contained commands in order, the important line looks like this after expansion:
ENDLOCAL & SET RESULT=4711
See, variable preserved and available in global context. Sometimes you want to preserve more than one variable, so you add additional SET statements. But be careful and do not insert a space before the ampersand - that space will contained in your variable!
ENDLOCAL & SET A=%A%& SET B=%B%& SET C=%C%
A little advise at the end: document your subroutines, otherwise you won't know what parameters it takes, what global variables it manipulates or requires or what value it "returns". I use double colons for subroutine documentation.
:: Shortens a string to the given number of characters.
:: PARAMS
:: 1 string to shorten
:: 2 number of characters
:: RESULT String shortened string
:SHSTR
SETLOCAL ENABLEDELAYEDEXPANSION
SET TEMP=%~1
ENDLOCAL & SET RESULT=!TEMP:~0,%~2!
GOTO:EOF
:: PARAMS
:: 1 string to shorten
:: 2 number of characters
:: RESULT String shortened string
:SHSTR
SETLOCAL ENABLEDELAYEDEXPANSION
SET TEMP=%~1
ENDLOCAL & SET RESULT=!TEMP:~0,%~2!
GOTO:EOF
See also this article about handling parameters and variables containing special characters.
cypressor - 27. Nov, 13:04