サブルーチンと戻り値
バッチファイルが大きくなってくると、同じ処理を何度も書くのは面倒ですし、バグの温床にもなります。そのような場合に役立つのがサブルーチン(疑似関数)です。
バッチファイルには他のプログラミング言語のような「関数定義」構文はありませんが、CALL :ラベル名 を使うことで関数に近い構造を実現できます。この記事では、サブルーチンの基本的な作り方から、引数の渡し方、数値・文字列の返し方まで順を追って解説します。
サブルーチンの基本構造
サブルーチンは次の要素で構成されます。
| 要素 | 説明 |
|---|---|
CALL :ラベル名 | サブルーチンを呼び出す |
:ラベル名 | サブルーチンの開始位置を示すラベル |
EXIT /B 終了コード | サブルーチンから呼び出し元へ戻る |
GOTO :EOF | EXIT /B 0 と同等。サブルーチンを終了する |
メインの処理とサブルーチンを分けるために、メインコードの末尾に GOTO :EOF を置くのが重要なポイントです。これがないと、処理がそのままサブルーチンのコードへ流れ込んでしまいます。
@echo off
setlocal
CALL :GREET "田中"
CALL :GREET "山田"
GOTO :EOF
:GREET
echo こんにちは、%~1 さん!
EXIT /B 0
:EOF は特殊なラベルで、ファイルの末尾を意味します。EXIT /B 0 と同様にサブルーチンを終了しますが、終了コードを指定するには EXIT /B N を使います。メインコード末尾での使用はどちらでも構いません。
サブルーチンはファイルのどこに書いても動作しますが、メインの処理の後(GOTO :EOF の後)にまとめて記述する慣習が一般的です。コードの可読性が向上します。
サブルーチンへの引数の渡し方
CALL :ラベル名 引数1 引数2 ... のように、ラベル名の後に続けて引数を渡せます。サブルーチン内では %1、%2… でアクセスします。
| 記法 | 意味 |
|---|---|
%1 | 1番目の引数(クォートを含む) |
%~1 | 1番目の引数(前後のクォートを除去) |
%2 | 2番目の引数 |
@echo off
setlocal
CALL :SHOW_INFO "山田" "東京" 30
GOTO :EOF
:SHOW_INFO
echo 名前: %~1
echo 都市: %~2
echo 年齢: %3
EXIT /B 0

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

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

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

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門
引数を "山田" のようにクォートで囲んで渡した場合、%1 では "山田"(クォート付き)、%~1 では 山田(クォートなし)になります。文字列を扱うときは %~1 を使う方が安全です。
数値の戻り値(ERRORLEVEL)
サブルーチンから数値を返すには、EXIT /B N で終了コード(N)を指定します。呼び出し元では %ERRORLEVEL% または IF ERRORLEVEL N で値を受け取ります。
慣習として、成功時は 0、エラー・失敗時は 1 以上の値を返します。
@echo off
setlocal
CALL :IS_EVEN 4
IF %ERRORLEVEL% EQU 0 (
echo 4 は偶数です。
) ELSE (
echo 4 は奇数です。
)
CALL :IS_EVEN 7
IF %ERRORLEVEL% EQU 0 (
echo 7 は偶数です。
) ELSE (
echo 7 は奇数です。
)
GOTO :EOF
:IS_EVEN
set /a CHECK=%1 %% 2
EXIT /B %CHECK%
set /a CHECK=%1 %% 2 の %% は、バッチファイル内で % 記号そのものを表すためのエスケープです。%1 %% 2 は「%1 を 2 で割った余り」を計算します。余りが 0 なら偶数、1 なら奇数です。
EXIT /B に数値を指定しない場合、その時点の ERRORLEVEL がそのまま返ります。意図しない値が返ることを防ぐため、EXIT /B 0 のように明示的に指定することを推奨します。
文字列の戻り値(endlocal & set トリック)
数値と違い、文字列の返却は少しトリッキーです。サブルーチン内で setlocal を使うと変数のスコープが閉じられるため、endlocal を呼んだ時点でサブルーチン内の変数はすべて消えてしまいます。
この問題を解決するのが endlocal & set 変数名=値 を 1 行にまとめるテクニックです。
なぜ 1 行にまとめると動くのか
コマンドプロンプトは 1 行を丸ごと解析してから実行します。endlocal & set RESULT=値 と書くと、set の右辺の値は endlocal が実行される前の状態で解析されます。つまり、サブルーチン内の変数の値が set の右辺に展開された後で endlocal が実行され、変数が呼び出し元のスコープに設定されるのです。
@echo off
setlocal
CALL :GET_GREETING "田中" RESULT
echo %RESULT%
GOTO :EOF
:GET_GREETING
setlocal
set NAME=%~1
set GREETING=こんにちは、%NAME% さん!
endlocal & set %~2=%GREETING%
EXIT /B 0
このパターンでは、第 2 引数(%~2)に戻り値を格納する変数名を渡しています。呼び出し元が変数名を自由に指定できるため、汎用性が高まります。
固定変数名を使うパターン
変数名を引数で渡す代わりに、固定の変数名(例: RESULT)を使う方法もシンプルでよく使われます。
@echo off
setlocal
CALL :FORMAT_DATE
echo 整形された日付: %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
endlocal & set 変数名=値 は必ず 1 行に書く必要があります。2 行に分けると endlocal の時点でサブルーチン内の変数が消えてしまい、set の値が空になります。
endlocal & set %~2=%VAL1% & set %~3=%VAL2% のように、1 行に複数の set を並べることで複数の値を同時に返せます。引数で変数名を受け取るパターンと組み合わせると便利です。
実践例:ファイルの存在確認サブルーチン
ここまでの知識を組み合わせた実践的な例として、複数ファイルの存在を確認し、不足ファイルの数を返すサブルーチンを示します。
@echo off
setlocal
CALL :CHECK_FILES
IF %ERRORLEVEL% NEQ 0 (
echo 必要なファイルが %ERRORLEVEL% 個不足しています。処理を中止します。
GOTO :EOF
)
echo すべてのファイルが確認できました。処理を開始します。
GOTO :EOF
:CHECK_FILES
setlocal enabledelayedexpansion
set MISSING=0
for %%f in (config.ini data.csv template.xlsx) do (
IF NOT EXIST "%%f" (
echo 警告: %%f が見つかりません。
set /a MISSING+=1
)
)
endlocal & exit /b %MISSING%
config.ini が存在しない場合の実行例:
すべてのファイルが存在する場合:
まとめ
| 操作 | 記法 |
|---|---|
| サブルーチンの呼び出し | CALL :ラベル名 引数1 引数2 ... |
| サブルーチンの終了 | EXIT /B 終了コード |
| 引数の参照 | %1、%~1(クォート除去) |
| 数値の返却 | EXIT /B N → 呼び出し元で %ERRORLEVEL% を参照 |
| 文字列の返却 | endlocal & set 変数名=値 を 1 行で実行 |
| フォールスルー防止 | メインコード末尾に GOTO :EOF を配置 |