Subroutines and Return Values

Maintained on

As a batch file grows, writing the same process over and over becomes tedious and also breeds bugs. Subroutines (pseudo-functions) are useful in such cases.

Batch files have no “function definition” syntax like other programming languages, but you can achieve a structure close to a function by using CALL :labelname. This article walks through subroutines step by step, from the basics of creating one to passing arguments and returning numbers and strings.

Basic Structure of a Subroutine

A subroutine consists of the following elements.

ElementDescription
CALL :labelnameCalls the subroutine
:labelnameA label that marks the start of the subroutine
EXIT /B exitcodeReturns from the subroutine to the caller
GOTO :EOFEquivalent to EXIT /B (the form without an exit code). Ends the subroutine

To separate the main processing from the subroutines, the important point is to place GOTO :EOF at the end of the main code. Without it, execution flows straight into the subroutine code.

@echo off
setlocal

CALL :GREET "Alice"
CALL :GREET "Bob"

GOTO :EOF

:GREET
echo Hello, %~1!
EXIT /B 0
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>basic-sub.cmd
Hello, Alice!
Hello, Bob!
C:\users\user>
About GOTO :EOF

:EOF is a special label that means the end of the file. GOTO :EOF ends the subroutine like EXIT /B, but you cannot specify an exit code, and the previous ERRORLEVEL is left as is. If you want to specify an exit code, use EXIT /B N. Either can be used at the end of the main code.

Where to place subroutines

Subroutines work wherever you write them in the file, but the common convention is to write them together after the main processing (after GOTO :EOF). This improves code readability.

Passing Arguments to a Subroutine

You can pass arguments after the label name, as in CALL :labelname arg1 arg2 .... Inside the subroutine, you access them with %1, %2, and so on.

NotationMeaning
%1The 1st argument (including quotes)
%~1The 1st argument (with surrounding quotes removed)
%2The 2nd argument
@echo off
setlocal

CALL :SHOW_INFO "Bob" "Tokyo" 30

GOTO :EOF

:SHOW_INFO
echo Name: %~1
echo City: %~2
echo Age: %3
EXIT /B 0
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>args-demo.cmd
Name: Bob
City: Tokyo
Age: 30
C:\users\user>
おすすめ書籍※ 広告を含む場合があります
中小企業経営者のためのRPA入門 RPA導入を成功させる方法

中小企業経営者のためのRPA入門 RPA導入を成功させる方法

60分でわかる! AIエージェント 超入門

60分でわかる! AIエージェント 超入門

コマンドラインの黒い画面が怖いんです。

コマンドラインの黒い画面が怖いんです。

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門

The difference between %~1 and %1

If you pass an argument enclosed in quotes, such as "Bob", %1 gives "Bob" (with quotes) and %~1 gives Bob (without quotes). When handling strings, it is safer to use %~1.

Numeric Return Values (ERRORLEVEL)

To return a number from a subroutine, specify an exit code (N) with EXIT /B N. The caller receives the value with %ERRORLEVEL% or IF ERRORLEVEL N.

By convention, return 0 on success and a value of 1 or greater on an error or failure.

@echo off
setlocal

CALL :IS_EVEN 4
IF %ERRORLEVEL% EQU 0 (
    echo 4 is even.
) ELSE (
    echo 4 is odd.
)

CALL :IS_EVEN 7
IF %ERRORLEVEL% EQU 0 (
    echo 7 is even.
) ELSE (
    echo 7 is odd.
)

GOTO :EOF

:IS_EVEN
set /a CHECK=%1 %% 2
EXIT /B %CHECK%
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>numeric-return.cmd
4 is even.
7 is odd.
C:\users\user>
The meaning of %%

The %% in set /a CHECK=%1 %% 2 is an escape that represents the % character itself in a batch file. %1 %% 2 calculates “the remainder of %1 divided by 2.” If the remainder is 0 the number is even, and if it is 1 the number is odd.

The exit code of EXIT /B

If you do not specify a number with EXIT /B, the current ERRORLEVEL is returned as is. To prevent unintended values from being returned, we recommend specifying it explicitly, as in EXIT /B 0.

String Return Values (The endlocal & set Trick)

Unlike numbers, returning a string is a little tricky. When you use setlocal inside a subroutine, the variable scope is closed, so all variables inside the subroutine disappear at the point you call endlocal.

The technique that solves this problem is to put endlocal & set VariableName=Value on one line.

Why Does It Work on One Line

Command Prompt parses an entire line before executing it. When you write endlocal & set RESULT=Value, the value on the right side of set is parsed before endlocal is executed. In other words, after the variable values inside the subroutine are expanded into the right side of set, endlocal runs, and the variable is set in the caller’s scope.

@echo off
setlocal

CALL :GET_GREETING "Alice" RESULT
echo %RESULT%

GOTO :EOF

:GET_GREETING
setlocal
set NAME=%~1
set GREETING=Hello, %NAME%!
endlocal & set "%~2=%GREETING%"
EXIT /B 0
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>string-return.cmd
Hello, Alice!
C:\users\user>

In this pattern, the second argument (%~2) is given the name of the variable that stores the return value. Because the caller can freely specify the variable name, this increases versatility.

Pattern Using a Fixed Variable Name

Instead of passing the variable name as an argument, a simple and commonly used method is to use a fixed variable name (for example, RESULT).

@echo off
setlocal

CALL :FORMAT_DATE
echo Formatted date: %RESULT%

GOTO :EOF

:FORMAT_DATE
setlocal
set YEAR=%DATE:~0,4%
set MONTH=%DATE:~5,2%
set DAY=%DATE:~8,2%
set LOCAL_RESULT=%YEAR%%MONTH%%DAY%
endlocal & set "RESULT=%LOCAL_RESULT%"
EXIT /B 0
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>fixed-return.cmd
Formatted date: 20260424
C:\users\user>
The format of %DATE% varies by environment

The format of %DATE% changes depending on regional settings. The example above assumes a yyyy/MM/dd format such as 2026/04/24, so the date cannot be extracted correctly in other environments. For a way to get the date independent of the environment, see the related article on getting the date and time.

It must be written on one line

endlocal & set VariableName=Value must be written on one line. If you split it into two lines, the variables inside the subroutine disappear at endlocal, and the value of set becomes empty.

Enclose the value in quotes

When another command follows on the same line (set VariableName=Value & ...), the space before the & becomes part of the value. Also, if the value contains special characters such as & or ), a syntax error may occur. Enclosing the whole assignment in quotes, as in set "VariableName=Value", avoids these problems. Handling values that contain % or ! is complicated, so it is safest to limit this technique to returning simple strings of alphanumeric characters or similar.

Returning multiple strings

By placing multiple set commands on one line, as in endlocal & set "%~2=%VAL1%" & set "%~3=%VAL2%", you can return multiple values at once. This is convenient when combined with the pattern of receiving variable names as arguments.

Practical Example: A File Existence Check Subroutine

As a practical example that combines what you have learned so far, here is a subroutine that checks the existence of multiple files and returns the number of missing files.

@echo off
setlocal

CALL :CHECK_FILES
IF %ERRORLEVEL% NEQ 0 (
    echo %ERRORLEVEL% required file(s) are missing. Aborting.
    GOTO :EOF
)
echo All files were found. Starting processing.

GOTO :EOF

:CHECK_FILES
setlocal
set MISSING=0
for %%f in (config.ini data.csv template.xlsx) do (
    IF NOT EXIST "%%f" (
        echo Warning: %%f was not found.
        set /a MISSING+=1
    )
)
endlocal & exit /b %MISSING%

On the last line, %MISSING% is expanded before endlocal runs, so the value of the variable inside the subroutine can be returned to the caller as the exit code.

Example run when config.ini does not exist:

- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>file-checker.cmd
Warning: config.ini was not found.
1 required file(s) are missing. Aborting.
C:\users\user>

When all files exist:

- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>file-checker.cmd
All files were found. Starting processing.
C:\users\user>

Summary

OperationNotation
Calling a subroutineCALL :labelname arg1 arg2 ...
Ending a subroutineEXIT /B exitcode
Referencing arguments%1, %~1 (removes quotes)
Returning a numberEXIT /B N → reference %ERRORLEVEL% in the caller
Returning a stringRun endlocal & set VariableName=Value on one line
Preventing fall-throughPlace GOTO :EOF at the end of the main code

Practice Questions

Practice Problem

Which technique is used to return a string value from a subroutine?

回答がサーバーに送信されることはありません
#Batch Files #Subroutines #call #ERRORLEVEL #setlocal #Command Prompt