Skip to main content

How to Pass Arguments to a Subroutine (CALL) in Batch Script

As batch scripts become more complex, you'll want to break your code into reusable pieces to avoid repetition and improve readability. The standard way to do this is by creating subroutines (also known as functions or labels). To make these subroutines truly useful, you need to pass information or arguments to them.

This guide will teach you how to use the CALL command to execute a subroutine and how to pass arguments to it. You will learn the simple syntax for accessing these arguments inside the subroutine and see a practical example of a reusable logging function.

What is a Subroutine?

In batch scripting, a subroutine is a block of code that starts with a label and ends with a GOTO :EOF.

  • Label: A line that starts with a colon, like :MySubroutine.
  • GOTO :EOF: This is a special command that means "Go to the End Of File." It acts as a return statement, telling the command processor to jump back to where it was called from.

Without subroutines, a script is just a single, long list of commands. With them, you can create structured, organized, and reusable code.

The Core Command: CALL

The CALL command is used to execute a subroutine. When the subroutine finishes (by reaching GOTO :EOF), the script execution continues on the line immediately after the CALL command.

The syntax for calling a subroutine with arguments is: CALL :SubroutineName Argument1 Argument2 "Argument with spaces"

How to Access Arguments Inside a Subroutine

Inside the subroutine, the arguments you passed are available using the same syntax as command-line arguments: %1, %2, %3, and so on.

  • %1: The first argument.
  • %2: The second argument.
  • %*: A special parameter that represents all arguments as a single string.

You can also use the tilde modifiers to manipulate these arguments, with the most useful being ~, which removes surrounding quotes.

  • %~1: The first argument, with any surrounding double quotes removed.

Basic Example: A Simple "Greeter" Subroutine

This script defines a simple subroutine that accepts a name and a city as arguments and prints a greeting.

@ECHO OFF
SETLOCAL

ECHO --- Main Script Logic ---
CALL :Greet "John Doe" "New York"
CALL :Greet "Jane Smith" "London"
ECHO -----------------------
ECHO.
ECHO Main script finished.

GOTO :EOF

REM =============================================
:Greet
REM This subroutine prints a greeting.
REM Argument 1 (%1): The user's name.
REM Argument 2 (%2): The user's city.
ECHO Hello, %~1! Welcome from %~2.
GOTO :EOF

Output:

--- Main Script Logic ---
Hello, John Doe! Welcome from New York.
Hello, Jane Smith! Welcome from London.
-----------------------

Main script finished.

This demonstrates how we can CALL the same piece of code multiple times with different data.

How Argument Passing Works

When you execute CALL :Greet "John Doe" "New York", the command processor does the following:

  1. It "bookmarks" its current location in the main script.
  2. It jumps to the :Greet label.
  3. It temporarily assigns the values to the parameters:
    • %1 becomes "John Doe".
    • %2 becomes "New York".
  4. It executes the commands inside the subroutine. The command ECHO Hello, %~1! uses ~1 to get the value of %1 without the quotes, resulting in John Doe.
  5. When it reaches GOTO :EOF, it jumps back to the bookmark it made in step 1 and continues with the next line.

Common Pitfalls and How to Solve Them

Problem: Handling Arguments with Spaces

If an argument contains spaces, you must enclose it in double quotes when you make the CALL.

Example of script with error:

REM This will FAIL. The script will see 4 arguments.
CALL :Greet John Doe New York
note

Inside the subroutine, %1 would be John, %2 would be Doe, etc., which is not what was intended.

Solution: Quote Arguments and Use ~

  1. Quote the arguments in the CALL command: CALL :Greet "John Doe" "New York".
  2. Use the tilde modifier (%~1) inside the subroutine to get the value without the quotes. This is a crucial best practice for writing robust subroutines.

Problem: Checking if an Argument was Provided

Sometimes, an argument is optional. Your subroutine needs a way to check if a value was actually passed to it.

Solution: Use a Quoted IF Statement

You can check if an argument is an empty string. The most reliable way is to add quotes or other characters to both sides of the comparison to handle the case where nothing was passed.

:MySubroutine
IF "%~1"=="" (
ECHO [ERROR] The first argument is required.
GOTO :EOF
)
ECHO The first argument is: %~1

This correctly handles the case where no argument is provided.

Practical Example: A Reusable Logging Function

This is one of the most useful subroutines you can create. It takes a message string as an argument and appends it to a log file with a timestamp.

@ECHO OFF
SETLOCAL
SET "LOG_FILE=%~dp0script.log"

CALL :Log "INFO" "Script execution started."
CALL :Log "WARN" "The configuration file was not found. Using defaults."
CALL :Log "INFO" "Processing complete."

GOTO :EOF

REM ===================================================================
:Log
REM This subroutine writes a timestamped message to the log file.
REM Argument 1 (%1): The log level (e.g., INFO, WARN, ERROR).
REM Argument 2 (%2): The message to log.
ECHO %DATE% %TIME% [%~1] - %~2 >> "%LOG_FILE%"
GOTO :EOF

This keeps the main logic of the script clean and centralizes the logging format in one place, making it easy to change later.

Conclusion

Subroutines are the key to writing structured, organized, and reusable batch scripts, and the CALL command is what makes them work.

Key takeaways for passing arguments:

  • Use CALL :SubroutineName Arg1 Arg2 ... to execute a subroutine with parameters.
  • Quote any arguments that contain spaces.
  • Inside the subroutine, access the arguments with %1, %2, etc.
  • Use the tilde modifier (%~1, %~2) to get the argument's value without the surrounding quotes.

By mastering subroutines and argument passing, you can stop writing long, repetitive scripts and start building modular, maintainable, and powerful automation tools.