バッチファイルの制御フローを支える3つの柱が ラベル(:LABEL)・GOTO・CALL です。「GOTO は悪い」と言われることもありますが、バッチファイルでは ループ・条件分岐・サブルーチン呼び出し のすべてにラベルと GOTO/CALL が登場します。本記事では命名規則・動作の違い・実務パターンを体系的に解説します。
この記事で学べること
- ラベル(:LABEL)の定義ルールと命名規則
- GOTO と CALL の決定的な違い(ジャンプ vs 戻る)
- GOTO を使ったループ・条件分岐・エラー処理
- CALL :ラベル で引数を渡して戻ってくる仕組み
- :EOF・GOTO :EOF の特殊な役割
- FOR ループ内で GOTO が使えない問題と回避策
ラベル(:LABEL)とは
ラベルはバッチファイル内のジャンプ先・処理開始点を示すマーカーです。行の先頭にコロン : を付けると、その行がラベルとして認識されます。
:START echo ここがSTARTラベルの処理 :ERROR_HANDLER echo ここがERROR_HANDLERラベルの処理
ラベルの命名規則
| ルール | 詳細 | 例 |
|---|---|---|
| 先頭文字 | コロン : で始める |
:MyLabel |
| 使える文字 | 英数字・アンダースコア・ハイフン・日本語も可 | :処理_A |
| スペース | ラベル名にスペースは使えない(ラベルが切れる) | NG: :My Label |
| 大文字小文字 | 区別しない(:start と :START は同じ) |
:Start = :START |
| スコープ | バッチファイル全体でグローバル(ブロックを越える) | FOR 内からでも参照可 |
| 重複定義 | 同名ラベルが複数あると最初のものが使われる | 重複は避けること |
ラベル名のベストプラクティス
ラベル名に文字数の厳密な制限はありませんが、短く・役割が明確な英数字名を付けるのが推奨です。
例: :PROC_BACKUP :ERR_HANDLER のように処理の役割が一目でわかる名前にすると、大規模バッチでも可読性が保たれます。
GOTO :ジャンプ(戻らない)
GOTO ラベル名 は指定したラベルへ一方通行でジャンプします。呼び出し元には戻りません。
@echo off echo 処理A GOTO SKIP echo ここはスキップされる :SKIP echo 処理B
実行結果:
処理A 処理B
GOTO の主な用途
| 用途 | パターン | 説明 |
|---|---|---|
| ループ | GOTO :ループラベル |
条件が偽になるまでループ先頭に戻る |
| 条件分岐のスキップ | GOTO :END |
else 相当処理を飛ばす |
| エラー処理へのジャンプ | GOTO :ERROR |
異常発生時にエラーハンドラへ飛ぶ |
| バッチ終了 | GOTO :EOF |
ファイル末尾(終了)へジャンプ |
CALL :ラベル :引数を渡して戻ってくる
CALL :ラベル名 引数1 引数2 は、指定したラベルへジャンプし、処理が終わると CALL した行の次に戻ってきます。これがサブルーチン呼び出しです。
@echo off setlocal echo メイン開始 CALL :Greet "山田" echo メイン終了 GOTO :EOF :Greet echo こんにちは、%~1さん! EXIT /B
実行結果:
メイン開始 こんにちは、山田さん! メイン終了
GOTO vs CALL の決定的な違い
| 比較項目 | GOTO :ラベル | CALL :ラベル |
|---|---|---|
| 呼び出し元に戻る | 戻らない(一方通行) | 戻る(EXIT /B で) |
| 引数(%1 %2) | 使えない | 使える |
| ERRORLEVEL 返却 | 不可 | 可(EXIT /B N) |
| 再帰呼び出し | 事実上不可能 | 可能 |
| 主な用途 | ループ・分岐・エラー処理 | 関数的な再利用処理 |
使い分けの鉄則
「処理後に元の場所に戻る必要があるか?」で判断します。
・戻る必要あり → CALL :ラベル
・戻る必要なし(ジャンプするだけ) → GOTO ラベル
:EOF という特殊ラベル
:EOF(End Of File)はバッチファイルに定義しなくても使える組み込みラベルです。
| 命令 | 動作 |
|---|---|
GOTO :EOF |
バッチファイルの末尾にジャンプ → バッチ終了 |
EXIT /B |
同上(CALL 内では呼び出し元に戻る) |
@echo off setlocal :: メイン処理 CALL :Process echo 完了 GOTO :EOF ← バッチここで終了。以降のラベルは実行されない :Process echo 処理中 EXIT /B
GOTO :EOF をメイン末尾に書く理由
メイン処理の後に GOTO :EOF を書かないと、サブルーチン定義部(:Process など)にそのまま流れ込んで実行されます。
メイン末尾の GOTO :EOF はサブルーチン定義部への意図しない流入を防ぐ「仕切り」です。
GOTO を使ったループパターン
バッチファイルには while 文がないため、GOTO でループを実装します。
カウンタループ
@echo off setlocal enabledelayedexpansion set /A COUNT=0 :LOOP if !COUNT! GEQ 5 GOTO :LOOP_END echo カウント: !COUNT! set /A COUNT+=1 GOTO :LOOP :LOOP_END echo ループ完了
入力待ちリトライループ
@echo off :INPUT_LOOP set /P INPUT=コマンドを入力(qで終了): if /I "%INPUT%"=="q" GOTO :EOF echo 入力された値: %INPUT% GOTO :INPUT_LOOP
GOTO を使った条件分岐パターン
IF 文と GOTO を組み合わせると、多段条件分岐が書けます。
@echo off setlocal set SCORE=75 if %SCORE% GEQ 90 GOTO :GRADE_A if %SCORE% GEQ 70 GOTO :GRADE_B if %SCORE% GEQ 50 GOTO :GRADE_C GOTO :GRADE_F :GRADE_A echo 評価: A(優) GOTO :EOF :GRADE_B echo 評価: B(良) GOTO :EOF :GRADE_C echo 評価: C(可) GOTO :EOF :GRADE_F echo 評価: F(不可) GOTO :EOF
各ラベルの末尾に GOTO :EOF を書く理由
:GRADE_A の処理が終わった後、GOTO :EOF がなければ :GRADE_B :GRADE_C に流れ込みます。
分岐先の各ブロックは必ず GOTO :EOF か EXIT /B で終わらせてください。
CALL でサブルーチンに引数を渡す
CALL でラベルを呼ぶとき、スペース区切りで引数を渡せます。サブルーチン内では %1 %2…(または %~1)で受け取ります。
@echo off setlocal CALL :ShowInfo "tanaka" 28 "東京" CALL :ShowInfo "suzuki" 35 "大阪" GOTO :EOF :ShowInfo set NAME=%~1 set AGE=%~2 set CITY=%~3 echo %NAME%(%AGE%歳)/ %CITY% EXIT /B
実行結果:
tanaka(28歳)/ 東京 suzuki(35歳)/ 大阪
引数修飾子一覧
| 記法 | 意味 | 例(%1 = “C:\data\file.txt”) |
|---|---|---|
%1 |
そのまま(クォート含む) | "C:\data\file.txt" |
%~1 |
クォートを除去 | C:\data\file.txt |
%~n1 |
ファイル名のみ(拡張子なし) | file |
%~x1 |
拡張子のみ | .txt |
%~d1 |
ドライブ文字のみ | C: |
%~p1 |
パスのみ(ドライブ除く) | \data\ |
%~f1 |
フルパス(絶対パス) | C:\data\file.txt |
%~z1 |
ファイルサイズ(バイト) | 1024 |
GOTO と CALL を組み合わせたエラー処理パターン
実務バッチでは「処理失敗 → エラーハンドラへ GOTO」と「ログ出力は CALL」を組み合わせるのが定番です。
@echo off setlocal set LOG=batch.log CALL :Log "INFO" "バッチ開始" xcopy /Y "source*" "dest" >nul 2>&1 if %ERRORLEVEL% NEQ 0 ( CALL :Log "ERROR" "xcopy失敗" GOTO :ERROR ) CALL :Log "INFO" "バッチ正常終了" EXIT /B 0 :ERROR echo エラーが発生しました。%LOG% を確認してください。 EXIT /B 1 :Log set LEVEL=%~1 set MSG=%~2 echo [%DATE% %TIME:~0,8%] [%LEVEL%] %MSG% echo [%DATE% %TIME:~0,8%] [%LEVEL%] %MSG% >> "%LOG%" EXIT /B
FOR ループ内で GOTO が使えない問題
FOR ループのブロック内(カッコ内)から GOTO を実行すると、FOR ループが中断されます。これは意図的な場合もありますが、ループを抜けずに次の繰り返しにスキップ(continue 相当)はできません。
:: NG例:FOR 内から GOTO でスキップしようとすると FOR ループ自体が終了する for %%F in (*.txt) do ( if "%%F"=="skip.txt" GOTO :NEXT_FILE ← ループ全体が終わる! echo 処理: %%F :NEXT_FILE )
回避策1:CALL :サブルーチン でラップする
@echo off for %%F in (*.txt) do ( CALL :ProcessFile "%%F" ) GOTO :EOF :ProcessFile if "%~1"=="skip.txt" EXIT /B ← サブルーチンを抜けるだけ(FOR は継続) echo 処理: %~1 EXIT /B
回避策2:IF のネストで条件を反転する
for %%F in (*.txt) do (
if NOT "%%F"=="skip.txt" (
echo 処理: %%F
)
)
FOR 内での制御フロー まとめ
・GOTO(FOR 内から) → FOR ループ全体を終了(break 相当)
・continue 相当(特定ファイルをスキップ)→ CALL :サブルーチン + EXIT /B で実現
・条件を満たす場合のみ処理 → IF NOT でネスト
実務パターン3選
パターン1:メニュー選択(GOTO による分岐)
@echo off :MENU echo =========================== echo 1. バックアップ実行 echo 2. ログ確認 echo 3. 終了 echo =========================== set /P CHOICE=番号を入力: if "%CHOICE%"=="1" GOTO :BACKUP if "%CHOICE%"=="2" GOTO :SHOW_LOG if "%CHOICE%"=="3" GOTO :EOF echo 無効な入力です GOTO :MENU :BACKUP echo バックアップを実行します... GOTO :MENU :SHOW_LOG type batch.log 2>nul || echo ログファイルがありません GOTO :MENU
パターン2:複数ファイルを CALL で処理(引数あり)
@echo off
setlocal
for %%F in (data*.csv) do (
CALL :ProcessCsv "%%F"
)
echo 全ファイル処理完了
GOTO :EOF
:ProcessCsv
set FILE=%~1
set BASENAME=%~n1
echo [処理] %BASENAME%.csv
copy "%FILE%" "backup\%BASENAME%_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.csv" >nul 2>&1
if %ERRORLEVEL% NEQ 0 (
echo [WARN] %BASENAME%.csv のバックアップ失敗
EXIT /B 1
)
EXIT /B 0
パターン3:引数の数に応じて分岐(CALL + 引数チェック)
@echo off setlocal :: コマンドライン引数で動作を変える if "%~1"=="" GOTO :USAGE if "%~1"=="/help" GOTO :USAGE if "%~1"=="/run" CALL :RunProcess "%~2" if "%~1"=="/check" CALL :CheckStatus GOTO :EOF :USAGE echo 使い方: batch.bat /run [ファイル名] または /check EXIT /B 1 :RunProcess echo 実行: %~1 EXIT /B 0 :CheckStatus echo ステータス確認中... EXIT /B 0
よくある質問(FAQ)
❓ GOTO と CALL の使い分けを一言で言うと? (クリックで開閉)
GOTO = 行き先通行・戻らない、CALL = 往復・戻ってくるです。
ループ・エラー処理へのジャンプ → GOTO、引数を受け取って結果を返す処理 → CALL :ラベル と覚えてください。
❓ ラベルに日本語は使える? (クリックで開閉)
技術的には使えますが、推奨しません。バッチファイルのエンコーディング(SJIS/UTF-8)や実行環境によって文字化けの原因になります。
ラベル名は 英数字+アンダースコア に統一するのがベストプラクティスです。
❓ CALL :ラベル と CALL バッチファイル名 の違いは? (クリックで開閉)
CALL :ラベル は同じバッチファイル内のラベルへジャンプして戻ります(サブルーチン)。
CALL other.bat は別のバッチファイルを実行して戻ります。いずれも実行後に呼び出し元に戻る点は同じです。
❓ GOTO :EOF と EXIT /B の違いは? (クリックで開閉)
GOTO :EOF はバッチ末尾へのジャンプで ERRORLEVEL を変更しません。
EXIT /B N は現在のコンテキストを終了して ERRORLEVEL に N をセットします。
戻り値を明示したいサブルーチン末尾は EXIT /B 0、メインの終了点は GOTO :EOF でも EXIT /B でもどちらでも構いません。統一することが重要です。
❓ 同じラベル名が2か所あるとどうなる? (クリックで開閉)
バッチファイルは最初に見つかったラベルにジャンプします。2つ目以降は無視されます。
ラベルの重複はデバッグが非常に難しくなるため、ラベル名はファイル内で必ず一意にしてください。
まとめ
| 命令 | 動作 | 引数 | 戻り値 | 主な用途 |
|---|---|---|---|---|
GOTO ラベル |
ジャンプ(戻らない) | なし | なし | ループ・分岐・エラー処理 |
CALL :ラベル |
呼び出し(EXIT /B で戻る) | %1〜%9 | ERRORLEVEL | 再利用処理・関数的呼び出し |
GOTO :EOF |
バッチ末尾にジャンプ | — | 変更なし | メイン終了・分岐後の終端 |
EXIT /B N |
現コンテキスト終了 | — | N を ERRORLEVEL にセット | サブルーチン終了 |
:ラベル名 |
ジャンプ先マーカー | — | — | GOTO/CALL の宛先定義 |
ラベル・GOTO・CALL は複雑に見えますが、「GOTO は一方通行、CALL は往復」という軸を押さえれば迷いません。まずはメイン末尾の GOTO :EOF、エラー処理の GOTO :ERROR、再利用処理の CALL :ログ出力 の3パターンを使いこなすだけでも、バッチファイルの品質は大きく向上します。

