- Part 1 – Getting Started
- Part 2 – Variables
- Part 3 – Return Codes
- Part 4 – stdin, stdout, stderr
- Part 5 – If/Then Conditionals
- Part 6 – Loops
- Part 7 – Functions
- Part 8 – Parsing Input
- Part 9 – Logging
- Part 10 – Advanced Tricks
Functions are de facto way to reuse code in just about any procedural coding language. While DOS lacks a bona fide function keyword, you can
fake it till you make it thanks to labels and the
There are a few gotchas to pay attention to:
- Your quasi functions need to be defined as labels at the bottom of your script.
- The main logic of your script must have a
EXIT /B [errorcode]statement. This keeps your main logic from falling through into your functions.
Defining a function
In this example, we’ll implement a poor man’s version of the *nix tee utility to write a message to both a file and the stdout stream. We’ll use a variable global to the entire script,
%log% in the function.
@ECHO OFF SETLOCAL :: script global variables SET me=%~n0 SET log=%TEMP%\%me%.txt :: The "main" logic of the script IF EXIST "%log%" DELETE /Q %log% >NUL :: do something cool, then log it CALL :tee "%me%: Hello, world!" :: force execution to quit at the end of the "main" logic EXIT /B %ERRORLEVEL% :: a function to write to a log file and write to stdout :tee ECHO %* >> "%log%" ECHO %* EXIT /B 0
Calling a function
We use the
CALL keyword to invoke the quasi function labelled
:tee. We can pass command line arguments just like we’re calling another batch file.
We have to remember to
EXIT /B keyword at the end our function. Sadly, there is no way to return anything other than an exit code.
The return value of
CALL will always be the exit code of the function. Like any other invokation of an executable, the caller reads
to get the exit code.
You have to get creative to pass anything other than integer return codes. The function can
ECHO to stdout, letting the caller decide to handle the
output by pipeling the output as the input to another executable, redirecting to a file, or parsing via the
The caller could also pass data by modifying a global variable, however, I try to avoid this approach.