Monday, July 2, 2007

Function Call In Batch

In any programming language, you can write functions or procedures to factor out a piece of code you use often.  In batch you can do the same.  Two ways you can do it.  You can create a separate batch file, and call it from inside the batch file you're scripting, or you can make it as an in-file function.

To call a separate batch file, it's really simple.  All you have to do is precede the batch name with the call operative, and you're good to go.  And example I like to use is to print a log entry both on console and into an output file.

REM PrLog.bat

REM  This batch file prints the message to the console and to an output file specified in the environment variable OUTP

echo %*

echo %* >> %OUTP%

REM End of file

Now every time we want to use this in a separate batch file all we have to do is call it after making sure you initialize the output file.

REM Initialize OUTP just once

set OUTP=%TEMP%\output.txt

del /y %OUTP%

REM Subsequently you just call the PrLog.bat with the message as arguments

call PrLog.bat Hello world 

Now this is nice and dandy, but you now have two files to watch for.  And if you need to copy your script, you need to make sure you don't forget to copy PrLog.bat too.

The more integrated way of doing this is to simply put the PrLog function in file.  To do this we need a label preceding the function, and end it with a goto :EOF or an exit /b.

REM Initialize OUTP just once

set OUTP=%TEMP%\output.txt

del /y %OUTP%

REM Subsequently you just call the :PRLOG function label with the message as arguments

call :PRLOG Hello world 

goto :EOF

REM End of main.


REM Start of function PrLog


echo %*

echo %* >> %OUTP%

goto :EOF

REM End of function PrLog

Note that the batch processor treats the :PRLOG as its own batch context, meaning when you goto :EOF at the end of that function, you don't go to the end of the current batch file, but you simple exit the function's batch context, which essentially means a function return.  You can also do an exit /b from the function if you need to return a meaningful return value to the caller signifying success or fail.


Nick said...

Even if you don't have the call :LABEL syntax introduced in Windows NT/XP, you can do much the same thing by calling %0:

@echo off
goto :$%1

:: ... main code ...
call %0 sub1
:: ... some more code ...
goto :eof

:: ... sub1 code ...
:: further arguments start with %2
goto :eof


Here I've picked $ as a sigil marking subroutine labels. It's unfortunate that old command processors will ignore label contents past the 8th character, so since $ takes one, you need to make your subroutine labels 7-character unique.

This is a great way to make multi-purpose scripts; you can call a specific subroutine directly from the commandline! You should also probably make the default :$ routine print a listing of the available routines.

This trick will work so long as you don't cd somewhere else, as %0 could be relative. Of course, you could use %~dpf0, but if you have the new extensions, you might as well use call :LABEL. :)

blognose said...

Hi Arif!

As this does not return a value, I'd call it a procedure call rather than a function call (both being method calls).

On another note, thanks for the nice, useful blog! :-)