【bat】複数バッチファイルを一括管理・実行するマスタースクリプト完全ガイド|順次・並列・エラー処理・ログ・再実行対応まで徹底解説

【bat】複数バッチファイルを一括管理・実行するマスタースクリプト完全ガイド|順次・並列・エラー処理・ログ・再実行対応まで徹底解説 bat

夜間バックアップ・定期レポート生成・システムメンテナンスなど、複数のバッチファイルを決まった順序で実行したい場面は多くあります。それぞれを個別に起動すると実行漏れやミスが起きやすく、エラーが発生しても気づかないまま後続処理が動いてしまうリスクがあります。

こうした問題を解決するのがマスタースクリプトです。複数のバッチファイルを「いつ・どの順で・どのパラメータで」実行するかを1か所で管理し、エラー時の制御・ログ記録・再実行まで一元化できます。

この記事では、単純な順次実行から始め、設定ファイルによる動的管理・並列実行・ドライランモード・失敗箇所からの再実行まで、実務レベルのパターンを体系的に解説します。CALL と START の違いを完全解説も合わせて読むと、CALL・STARTの動作の違いを深く理解できます。

この記事で学べること
CALL による順次実行の基本と注意点、設定ファイル(リスト)による実行スクリプト管理、エラー時の中断・スキップ・集計パターン、タイムスタンプ付きログと経過時間の記録、START による並列実行と完了待ち、ドライランモード(–dry-run)の実装、失敗したスクリプトだけ再実行する再開機能、本番運用に耐える完成版マスタースクリプト
スポンサーリンク

マスタースクリプトとは何か

マスタースクリプトとは、複数のバッチファイルを呼び出す「指揮者」となる上位スクリプトです。各バッチファイル(子バッチ)は自分の処理だけに集中し、マスタースクリプトが実行順序・引数・エラー処理・ログ管理を一手に担います。

比較項目 個別実行(マスターなし) マスタースクリプト
実行漏れ 手動確認が必要 自動化・漏れゼロ
順序管理 ドキュメントに依存 スクリプト自体が仕様書
エラー検知 見落としやすい ERRORLEVEL で即時検知・記録
ログ 各バッチに散在 一元化して追跡しやすい
再実行 全件やり直しか手動で対応 失敗箇所から再開できる
変更コスト 実行手順書も更新が必要 スクリプト1か所だけ変更

基本構造:CALL による順次実行

最もシンプルな形は call で子バッチを順番に呼び出す方式です。call は子バッチの完了を待ってから次の処理に進むため、処理の順序が保証されます。CALL と START の違いを完全解説で詳しく解説していますが、start(非同期)との違いを把握しておくことが重要です。

基本の順次実行(master_basic.bat)
@echo off
setlocal enabledelayedexpansion

:: ── 設定 ──────────────────────────────────────────────
set "BASE_DIR=%~dp0"
set "LOG_DIR=%BASE_DIR%logs"
set "LOG_FILE=%LOG_DIR%\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"

:: ── 開始ログ ──────────────────────────────────────────
echo ================================================ >> "%LOG_FILE%"
echo  マスター実行開始: %DATE% %TIME%                >> "%LOG_FILE%"
echo ================================================ >> "%LOG_FILE%"
echo マスター実行開始: %DATE% %TIME%

:: ── 子バッチを順次実行 ──────────────────────────────────
call "%BASE_DIR%01_init.bat"       >> "%LOG_FILE%" 2>&1
call "%BASE_DIR%02_fetch.bat"      >> "%LOG_FILE%" 2>&1
call "%BASE_DIR%03_process.bat"    >> "%LOG_FILE%" 2>&1
call "%BASE_DIR%04_report.bat"     >> "%LOG_FILE%" 2>&1
call "%BASE_DIR%05_cleanup.bat"    >> "%LOG_FILE%" 2>&1

:: ── 完了ログ ──────────────────────────────────────────
echo  マスター実行完了: %DATE% %TIME%                >> "%LOG_FILE%"
echo マスター実行完了: %DATE% %TIME%

endlocal
CALL なしで呼ぶと処理が戻らない
call を省略して子バッチのファイル名を直接書くと(例:01_init.bat)、子バッチが終了した時点でマスタースクリプトも終了します。後続の処理は実行されません。子バッチを呼ぶ場合は必ず call を付けるのが鉄則です。

ERRORLEVEL をチェックしながら実行する

子バッチが失敗したとき即座に処理を止めるには、各 call の後に ERRORLEVEL を確認します。エラー時に処理を中断・終了する方法の解説も参考にしてください。

エラー検知付き順次実行
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LOG=%~dp0logs\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
if not exist "%~dp0logs" mkdir "%~dp0logs"

set /a FAIL_COUNT=0

call :run_step "01_init.bat"
call :run_step "02_fetch.bat"
call :run_step "03_process.bat"
call :run_step "04_report.bat"

if !FAIL_COUNT! GTR 0 (
    echo [RESULT] !FAIL_COUNT!件のエラーが発生しました。
    echo [RESULT] !FAIL_COUNT!件のエラーが発生しました。 >> "%LOG%"
    exit /b 1
)
echo [RESULT] すべて正常終了しました。
echo [RESULT] すべて正常終了しました。 >> "%LOG%"
exit /b 0

:: ── サブルーチン: 1ステップ実行 ──────────────────────
:run_step
setlocal
set "SCRIPT=%BASE%%~1"
set "START_TIME=%TIME%"

echo [START] %~1 (%TIME%) >> "%LOG%"
call "%SCRIPT%" >> "%LOG%" 2>&1
set /a ERR=%ERRORLEVEL%

if %ERR% NEQ 0 (
    echo [ERROR] %~1 - ERRORLEVEL=%ERR% (%TIME%) >> "%LOG%"
    echo [ERROR] %~1 がエラー終了しました(コード: %ERR%)
    endlocal & set /a FAIL_COUNT+=1
    exit /b %ERR%
)
echo [OK]    %~1 (%TIME%) >> "%LOG%"
echo [OK]    %~1

endlocal
exit /b 0
ERRORLEVEL を変数に保存するタイミング
call の直後に set /a ERR=%ERRORLEVEL% で値を変数に保存してください。echo など別のコマンドを挟むと ERRORLEVEL が上書きされます。詳細はsetlocal enabledelayedexpansion 完全ガイドの「ERRORLEVEL保存」の項も参照してください。

設定ファイル(リスト)による動的なスクリプト管理

実行するスクリプトをコード中に直書きすると、追加・削除のたびにマスタースクリプトを修正する必要があります。設定ファイルにスクリプトのリストを書き、マスタースクリプトはそれを読み込む方式にするとスクリプト本体を変更せずに実行内容を変えられます

scripts_list.txt(実行リスト)
:: 先頭が :: の行はコメント(スキップされる)
:: 書式: スクリプト名 [引数1] [引数2] ...

01_init.bat
02_fetch.bat  --date 2024-01-15
03_process.bat
:: 04_legacy.bat  ← コメントアウトで無効化
05_report.bat --format pdf
06_cleanup.bat
設定ファイルを読み込んで実行するマスタースクリプト
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LIST=%BASE%scripts_list.txt"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"
if not exist "%LIST%" (
    echo [ERROR] 実行リストが見つかりません: %LIST%
    exit /b 1
)

set /a STEP=0
set /a OK_COUNT=0
set /a FAIL_COUNT=0

echo =============================== >> "%LOG%"
echo 開始: %DATE% %TIME% >> "%LOG%"

:: リストを1行ずつ読み込んで実行
for /f "usebackq eol=: delims=" %%L in ("%LIST%") do (
    :: 空行をスキップ
    if not "%%L"=="" (
        set /a STEP+=1

        :: 最初のトークンがスクリプト名、残りが引数
        for /f "tokens=1*" %%A in ("%%L") do (
            set "SCRIPT=%%A"
            set "ARGS=%%B"
        )

        echo [Step !STEP!] !SCRIPT! !ARGS! >> "%LOG%"
        echo [Step !STEP!] !SCRIPT! !ARGS!

        call "%BASE%!SCRIPT!" !ARGS! >> "%LOG%" 2>&1
        set /a ERR=!ERRORLEVEL!

        if !ERR! NEQ 0 (
            echo [FAIL] !SCRIPT! エラーコード: !ERR! >> "%LOG%"
            set /a FAIL_COUNT+=1
            :: エラー時は中断する場合は以下を有効化
            :: exit /b 1
        ) else (
            set /a OK_COUNT+=1
        )
    )
)

echo ─────────────────────────── >> "%LOG%"
echo 完了: %DATE% %TIME% >> "%LOG%"
echo 成功: %OK_COUNT% / 失敗: %FAIL_COUNT% >> "%LOG%"
echo 成功: %OK_COUNT% / 失敗: %FAIL_COUNT%

if !FAIL_COUNT! GTR 0 exit /b 1
exit /b 0

endlocal
eol=: でコメント行をスキップする
for /f "eol=: delims="eol=: は指定した文字で始まる行をスキップするオプションです。: を指定するとバッチ形式のコメント行(:: コメント)を自動的に無視できます。設定ファイルに # でコメントを書く場合は eol=# に変更してください。

エラー処理の3つの戦略

子バッチでエラーが発生したとき、どう対応するかは用途によって変わります。代表的な3つの戦略を比較します。

戦略 use case 実装のポイント
即時中断 DB更新など依存関係が強い処理 if ERR NEQ 0 exit /b 1
スキップして継続 独立した処理の集まり(レポート生成など) FAIL_COUNT を加算して最後に集計
リトライ後に継続 一時的な障害(ネットワーク・ロック)が起きやすい処理 リトライループ後に成否を判定

戦略1:エラー即時中断(依存関係のある処理向け)

エラー即時中断パターン
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"

:: 依存関係: Step1 が成功しなければ Step2 は意味がない
call "%BASE%step1_db_migrate.bat"
set /a ERR=!ERRORLEVEL!
if !ERR! NEQ 0 (
    echo [ABORT] step1_db_migrate.bat が失敗。後続処理を中断します。
    exit /b !ERR!
)

call "%BASE%step2_data_import.bat"
set /a ERR=!ERRORLEVEL!
if !ERR! NEQ 0 (
    echo [ABORT] step2_data_import.bat が失敗。後続処理を中断します。
    exit /b !ERR!
)

call "%BASE%step3_verify.bat"
echo 全ステップ完了

endlocal

戦略2:スキップして最後にまとめて報告(独立した処理向け)

スキップ継続 + 最終集計パターン
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LOG=%~dp0logs\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
if not exist "%~dp0logs" mkdir "%~dp0logs"

set /a OK=0
set /a FAIL=0
set "FAILED_LIST="

:: 独立したレポート生成処理(各部門ごとなど)
for %%S in (
    dept_a_report.bat
    dept_b_report.bat
    dept_c_report.bat
    dept_d_report.bat
) do (
    call "%BASE%%%S" >> "%LOG%" 2>&1
    if !ERRORLEVEL! NEQ 0 (
        set /a FAIL+=1
        set "FAILED_LIST=!FAILED_LIST! %%S"
        echo [SKIP] %%S 失敗 - 継続します
    ) else (
        set /a OK+=1
        echo [OK]   %%S
    )
)

echo ==================
echo 結果: 成功 %OK% / 失敗 %FAIL%
if !FAIL! GTR 0 (
    echo 失敗したスクリプト: !FAILED_LIST!
    exit /b 1
)

endlocal

戦略3:リトライ付き実行(一時障害に強い)

リトライ付き実行サブルーチン
@echo off
setlocal enabledelayedexpansion

call :run_with_retry "network_backup.bat" 3 15
if errorlevel 1 echo [ALERT] ネットワークバックアップが最終的に失敗しました。
exit /b %errorlevel%

:: ── サブルーチン: リトライ付き実行 ──────────────────────
:: 引数: %1=スクリプト名  %2=最大試行回数  %3=リトライ待機秒
:run_with_retry
setlocal
set "SCR=%~1"
set /a MAX=%~2
set /a WAIT=%~3
set /a CNT=0

:retry_loop
set /a CNT+=1
echo [試行 %CNT%/%MAX%] %SCR%
call "%~dp0%SCR%"
set /a ERR=!ERRORLEVEL!

if !ERR! EQU 0 (
    echo [OK] %SCR% 成功(%CNT%回目)
    endlocal & exit /b 0
)
if !CNT! LSS !MAX! (
    echo [WAIT] 失敗。%WAIT%秒後にリトライ...
    timeout /t !WAIT! /nobreak > nul
    goto :retry_loop
)
echo [FAIL] %SCR% %MAX%回試行しましたが失敗しました。
endlocal & exit /b 1

実行ログの充実:タイムスタンプ・経過時間・集計

マスタースクリプトのログは「いつ・どのスクリプトが・何秒かかったか・成功したか」を記録することが重要です。バッチファイルで実行時刻・経過時間をログに記録する完全ガイドの経過時間計測を組み合わせると、ボトルネックの特定にも役立ちます。

経過時間付きログ出力サブルーチン
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LOG=%~dp0logs\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
if not exist "%~dp0logs" mkdir "%~dp0logs"

:: ── 実行リスト ────────────────────────────────────────
call :timed_run "01_init.bat"
call :timed_run "02_fetch.bat"
call :timed_run "03_process.bat"

exit /b 0

:: ── サブルーチン: 経過時間計測付き実行 ──────────────────
:timed_run
setlocal
set "SCRIPT=%~1"

:: 開始時刻を記録(HH:MM:SS → 秒に変換)
for /f "tokens=1-3 delims=:." %%H in ("%TIME: =0%") do (
    set /a T_START=%%H*3600 + %%I*60 + %%J
)
set "START_STR=%TIME%"

echo [START] %SCRIPT% (%START_STR%) >> "%LOG%"
call "%BASE%%SCRIPT%" >> "%LOG%" 2>&1
set /a ERR=!ERRORLEVEL!

:: 終了時刻・経過秒数
for /f "tokens=1-3 delims=:." %%H in ("%TIME: =0%") do (
    set /a T_END=%%H*3600 + %%I*60 + %%J
)
set /a ELAPSED=T_END - T_START
if !ELAPSED! LSS 0 set /a ELAPSED+=86400

if !ERR! NEQ 0 (
    echo [FAIL] %SCRIPT% - %ELAPSED%秒 - ERRORLEVEL=!ERR! >> "%LOG%"
    echo [FAIL] %SCRIPT% - %ELAPSED%秒
    endlocal & exit /b !ERR!
)
echo [OK]   %SCRIPT% - %ELAPSED%秒 >> "%LOG%"
echo [OK]   %SCRIPT% - %ELAPSED%秒

endlocal & exit /b 0

START による並列(非同期)実行

互いに依存関係のない処理は並列実行するとトータルの実行時間を大幅に短縮できます。start /b で子バッチをバックグラウンド起動し、すべての完了を待ってから次のステップに進みます。バッチファイルで並列処理を実行する方法大量ファイルを並列処理する方法完全ガイドで詳しいパターンを解説しています。

並列実行 + 全完了待ち
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LOCK_DIR=%TEMP%\master_locks_%RANDOM%"
mkdir "%LOCK_DIR%"

:: ── 並列実行(独立した処理を同時起動)────────────────
echo 並列実行開始...

:: ロックファイルを使って完了を検知
start /b cmd /c "call ""%BASE%dept_a_report.bat"" && del ""%LOCK_DIR%\dept_a.lock"" 2>nul || del ""%LOCK_DIR%\dept_a.lock"" 2>nul"
start /b cmd /c "call ""%BASE%dept_b_report.bat"" && del ""%LOCK_DIR%\dept_b.lock"" 2>nul || del ""%LOCK_DIR%\dept_b.lock"" 2>nul"
start /b cmd /c "call ""%BASE%dept_c_report.bat"" && del ""%LOCK_DIR%\dept_c.lock"" 2>nul || del ""%LOCK_DIR%\dept_c.lock"" 2>nul"

:: ロックファイルが全部消えるまで待機(最大 300 秒)
for %%K in (dept_a dept_b dept_c) do (
    copy nul "%LOCK_DIR%\%%K.lock" > nul
)

set /a WAIT=0
:wait_loop
timeout /t 2 /nobreak > nul
set /a WAIT+=2
:: ロックファイルがなくなったか確認
set "REMAIN="
for %%K in (dept_a dept_b dept_c) do (
    if exist "%LOCK_DIR%\%%K.lock" set "REMAIN=1"
)
if defined REMAIN (
    if !WAIT! LSS 300 goto :wait_loop
    echo [TIMEOUT] 並列処理がタイムアウトしました。
    rmdir /s /q "%LOCK_DIR%" 2>nul
    exit /b 1
)

echo 全並列処理完了 (%WAIT%秒)
rmdir /s /q "%LOCK_DIR%" 2>nul

:: ── 並列結果に依存する後続処理 ────────────────────────
call "%BASE%aggregate_report.bat"

endlocal
同時実行数を制限したい場合
バッチファイルで並列処理を実行する方法の「スロット制御」パターンを使うと、同時実行数の上限(例:4本まで)を設定できます。PCのCPUコア数やI/O負荷を考慮して上限を決めてください。

子バッチへの引数渡し

マスタースクリプトから子バッチに引数を渡すことで、同じ子バッチを異なる設定で呼び出せます。バッチファイルで引数を渡す方法完全ガイドで引数の受け取り方の詳細を確認できます。

引数を渡して同一バッチを複数設定で実行
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"

:: 同一スクリプトを異なる対象で複数回実行
call "%BASE%process_data.bat" --input "C:\data\dept_a" --output "C:\reports\dept_a" --format csv
if !ERRORLEVEL! NEQ 0 echo [ERROR] dept_a処理失敗

call "%BASE%process_data.bat" --input "C:\data\dept_b" --output "C:\reports\dept_b" --format pdf
if !ERRORLEVEL! NEQ 0 echo [ERROR] dept_b処理失敗

call "%BASE%process_data.bat" --input "C:\data\dept_c" --output "C:\reports\dept_c" --format excel
if !ERRORLEVEL! NEQ 0 echo [ERROR] dept_c処理失敗

endlocal
設定ファイルから引数を渡すパターン
:: run_config.txt
:: 書式: スクリプト名 引数1 引数2 ...
process_data.bat --input C:\data\dept_a --output C:\reports\dept_a
process_data.bat --input C:\data\dept_b --output C:\reports\dept_b
notify.bat       --to admin@example.com --subject "定期レポート完了"
設定ファイルから引数を読み込んで実行
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "CONFIG=%BASE%run_config.txt"

for /f "usebackq eol=: tokens=1* delims= " %%A in ("%CONFIG%") do (
    :: %%A = スクリプト名, %%B = 残りの引数すべて
    echo [RUN] %%A %%B
    call "%BASE%%%A" %%B
    if !ERRORLEVEL! NEQ 0 (
        echo [FAIL] %%A %%B
    )
)

endlocal

ドライランモード(–dry-run)の実装

本番実行前に「どのスクリプトがどの順序で実行されるか」を確認できるドライランモードを実装すると、オペレーションミスを防げます。

ドライランモード付きマスタースクリプト
@echo off
setlocal enabledelayedexpansion

:: ── 引数解析 ──────────────────────────────────────────
set "DRY_RUN=0"
set "VERBOSE=0"

:parse_args
if "%~1"=="" goto :args_done
if /i "%~1"=="--dry-run"  set "DRY_RUN=1"
if /i "%~1"=="-n"         set "DRY_RUN=1"
if /i "%~1"=="--verbose"  set "VERBOSE=1"
if /i "%~1"=="-v"         set "VERBOSE=1"
shift
goto :parse_args
:args_done

if %DRY_RUN%==1 (
    echo [DRY RUN] 実際の実行は行いません。実行予定のスクリプト一覧:
    echo ──────────────────────────────────────
)

set "BASE=%~dp0"
set "LIST=%BASE%scripts_list.txt"
set /a STEP=0

for /f "usebackq eol=: delims=" %%L in ("%LIST%") do (
    if not "%%L"=="" (
        set /a STEP+=1
        for /f "tokens=1*" %%A in ("%%L") do (
            if %DRY_RUN%==1 (
                echo   [Step !STEP!] %%A  引数: %%B
            ) else (
                echo [Step !STEP!] 実行: %%A %%B
                call "%BASE%%%A" %%B
                if !ERRORLEVEL! NEQ 0 (
                    echo [FAIL] %%A が失敗しました。
                    if %VERBOSE%==1 echo         引数: %%B
                )
            )
        )
    )
)

if %DRY_RUN%==1 (
    echo ──────────────────────────────────────
    echo [DRY RUN] 合計 %STEP% ステップが実行される予定です。
    echo            実際に実行するには --dry-run を外してください。
)

endlocal
使用例
:: 実行内容を確認するだけ(実際は動かない)
master.bat --dry-run

:: 実行内容を確認(詳細表示)
master.bat --dry-run --verbose

:: 実際に実行
master.bat

失敗したスクリプトだけ再実行する「再開機能」

大量のバッチを順次実行している途中で障害が発生した場合、最初からやり直すのは非効率です。前回処理の状態を記録して次回に引き継ぐ方法で詳しく解説している「前回の状態を次回に引き継ぐ」パターンを応用し、失敗したステップだけを再実行できる仕組みを作ります。

再開機能付きマスタースクリプト
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "LIST=%BASE%scripts_list.txt"
set "STATE_FILE=%BASE%.master_state"
set "LOG=%BASE%logs\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
if not exist "%BASE%logs" mkdir "%BASE%logs"

:: ── 引数: --resume で前回失敗箇所から再開 ─────────────
set "RESUME=0"
if /i "%~1"=="--resume" set "RESUME=1"

:: 前回の状態ファイルを読み込む
set "LAST_FAIL="
if %RESUME%==1 (
    if exist "%STATE_FILE%" (
        for /f "usebackq delims=" %%L in ("%STATE_FILE%") do set "LAST_FAIL=%%L"
        echo [RESUME] 前回失敗: !LAST_FAIL! から再開します。
    )
)

set /a STEP=0
set /a SKIP=0
set "SKIP_DONE=0"

for /f "usebackq eol=: delims=" %%L in ("%LIST%") do (
    if not "%%L"=="" (
        set /a STEP+=1
        for /f "tokens=1*" %%A in ("%%L") do (
            set "CURR=%%A"

            :: リジューム: 失敗スクリプトに到達するまでスキップ
            if %RESUME%==1 if "!SKIP_DONE!"=="0" (
                if /i "!CURR!"=="!LAST_FAIL!" (
                    set "SKIP_DONE=1"
                ) else (
                    echo [SKIP] !CURR! (前回成功済み)
                    goto :step_next
                )
            )

            echo [Step !STEP!] !CURR! %%B
            call "%BASE%!CURR!" %%B >> "%LOG%" 2>&1
            set /a ERR=!ERRORLEVEL!

            if !ERR! NEQ 0 (
                echo [FAIL] !CURR! エラーコード: !ERR!
                :: 失敗したスクリプト名を状態ファイルに記録
                echo !CURR! > "%STATE_FILE%"
                echo 再実行するには: master.bat --resume
                exit /b !ERR!
            )
            echo [OK]   !CURR!
        )
        :step_next
    )
)

:: すべて成功したら状態ファイルを削除
if exist "%STATE_FILE%" del "%STATE_FILE%"
echo すべてのステップが正常に完了しました。

endlocal
使用例
:: 通常実行
master.bat

:: 前回失敗したスクリプトから再開
master.bat --resume

本番運用向け:完成版マスタースクリプト

これまでの要素をすべて統合した、実際の業務で使える完成形スクリプトです。ドライラン・設定ファイル管理・経過時間ログ・リトライ・再開機能・完了通知を含んでいます。

master_full.bat(完成版)
@echo off
setlocal enabledelayedexpansion
:: ============================================================
:: master_full.bat - 本番運用向けマスタースクリプト
::
:: 使い方:
::   master_full.bat            通常実行
::   master_full.bat --dry-run  実行予定確認のみ
::   master_full.bat --resume   前回失敗箇所から再開
:: ============================================================

:: ── 設定 ────────────────────────────────────────────────
set "BASE=%~dp0"
set "LIST=%BASE%scripts_list.txt"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%\master_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%_%TIME:~0,2%%TIME:~3,2%.log"
set "STATE=%BASE%.master_state"
set /a RETRY_MAX=2
set /a RETRY_WAIT=10

:: ── 引数解析 ──────────────────────────────────────────────
set "DRY=0"
set "RESUME=0"
:arg_loop
if "%~1"=="" goto :arg_end
if /i "%~1"=="--dry-run" set "DRY=1"
if /i "%~1"=="--resume"  set "RESUME=1"
shift & goto :arg_loop
:arg_end

:: ── 初期化 ──────────────────────────────────────────────
if not exist "%LOGDIR%" mkdir "%LOGDIR%"
if not exist "%LIST%" (
    echo [FATAL] 実行リストが見つかりません: %LIST% & exit /b 1
)

:: ── ドライラン時は内容表示のみ ─────────────────────────
if %DRY%==1 (
    echo [DRY RUN MODE] 実行予定スクリプト一覧:
    set /a N=0
    for /f "usebackq eol=: delims=" %%L in ("%LIST%") do (
        if not "%%L"=="" (
            set /a N+=1
            echo   !N!. %%L
        )
    )
    echo [DRY RUN] 合計 !N! ステップ
    exit /b 0
)

:: ── 実行開始 ──────────────────────────────────────────────
echo ======================================== >> "%LOG%"
echo  MASTER START: %DATE% %TIME%            >> "%LOG%"
echo  LIST: %LIST%                           >> "%LOG%"
echo ======================================== >> "%LOG%"

:: リジューム: 前回失敗スクリプトを読み込む
set "RESUME_FROM="
if %RESUME%==1 (
    if exist "%STATE%" (
        for /f "usebackq delims=" %%R in ("%STATE%") do set "RESUME_FROM=%%R"
        echo [RESUME] !RESUME_FROM! から再開 >> "%LOG%"
    )
)

set /a STEP=0
set /a OK=0
set /a FAIL=0
set "SKIP_DONE=0"
set "FAILED_LIST="

for /f "usebackq eol=: delims=" %%L in ("%LIST%") do (
    if not "%%L"=="" (
        set /a STEP+=1
        for /f "tokens=1*" %%A in ("%%L") do (
            set "SCR=%%A" & set "ARGS=%%B"
        )

        :: リジュームスキップ処理
        if %RESUME%==1 if "!SKIP_DONE!"=="0" (
            if /i "!SCR!"=="!RESUME_FROM!" (
                set "SKIP_DONE=1"
            ) else (
                echo [SKIP] Step !STEP! !SCR! >> "%LOG%"
                goto :next_step
            )
        )

        :: 経過時間計測: 開始
        for /f "tokens=1-3 delims=:." %%H in ("!TIME: =0!") do set /a TS=%%H*3600+%%I*60+%%J

        echo [Step !STEP!] !SCR! !ARGS! >> "%LOG%"
        echo [Step !STEP!] !SCR!

        :: リトライ付き実行
        set /a TRY=0
        :retry
        set /a TRY+=1
        call "%BASE%!SCR!" !ARGS! >> "%LOG%" 2>&1
        set /a ERR=!ERRORLEVEL!

        if !ERR! NEQ 0 (
            if !TRY! LSS %RETRY_MAX% (
                echo [RETRY !TRY!/%RETRY_MAX%] !SCR! ... >> "%LOG%"
                timeout /t %RETRY_WAIT% /nobreak > nul
                goto :retry
            )
            :: 最終失敗
            set /a FAIL+=1
            set "FAILED_LIST=!FAILED_LIST! !SCR!"
            echo [FAIL] !SCR! ERR=!ERR! >> "%LOG%"
            echo [FAIL] !SCR! エラーコード=!ERR!
            echo !SCR! > "%STATE%"
            goto :next_step
        )

        :: 経過時間計測: 終了
        for /f "tokens=1-3 delims=:." %%H in ("!TIME: =0!") do set /a TE=%%H*3600+%%I*60+%%J
        set /a ELAPSED=TE-TS
        if !ELAPSED! LSS 0 set /a ELAPSED+=86400

        set /a OK+=1
        echo [OK]   !SCR! - !ELAPSED!秒 >> "%LOG%"
        echo [OK]   !SCR! - !ELAPSED!秒

        :next_step
    )
)

:: ── 結果サマリー ────────────────────────────────────────
echo ======================================== >> "%LOG%"
echo  RESULT: 成功=%OK% 失敗=%FAIL%          >> "%LOG%"
echo  終了: %DATE% %TIME%                    >> "%LOG%"
echo ======================================== >> "%LOG%"

echo.
echo ────────────────────────────────────────
echo  実行結果: 成功 %OK% / 失敗 %FAIL%
if defined FAILED_LIST echo  失敗したスクリプト: !FAILED_LIST!
echo  ログ: %LOG%
echo ────────────────────────────────────────

if !FAIL! GTR 0 (
    echo 失敗があります。master_full.bat --resume で再実行できます。
    exit /b 1
)
if exist "%STATE%" del "%STATE%"
exit /b 0

endlocal
通知連携も追加できる
最終の結果サマリー部分に バッチファイルでエラー通知メールを自動送信する完全ガイド の PowerShell 通知コードを組み込むことで、実行完了・エラー発生時に自動でメールや Slack 通知を送れます。また schtasksコマンドでタスクスケジューラを完全制御する完全ガイド と組み合わせると、このマスタースクリプトを夜間に自動実行する設定も簡単です。

よくある質問(FAQ)

QCALLとSTARTはどちらを使うべきですか?
A処理の順序が重要な場合(前の処理の結果を次で使う・DBの整合性など)は CALL(同期実行)、互いに独立していてトータル時間を短縮したい場合は START /B(非同期実行)を使います。START は完了を待たないため、別途「完了待ち」処理が必要です。詳しくはCALL と START の違いを完全解説を参照してください。
Q子バッチでSETした変数をマスタースクリプトに引き継げますか?
A通常は引き継げません。call で呼び出した子バッチが setlocal を使っていれば変数はローカルスコープです。値を引き継ぐには、①一時ファイルに書き込んで親で読み込む、②子バッチの exit /b の終了コードを使う(数値のみ)、③子バッチで setlocal を使わずに直接変数を設定する(非推奨)、の3つの方法があります。
Q子バッチが無限ループした場合、マスタースクリプトはどうなりますか?
ACALL で呼ばれた子バッチが終了しない限り、マスタースクリプトも止まります。バッチファイルに実行タイムアウトを設定して自動終了する完全ガイドのタイムアウト機能(2バッチ方式)を組み合わせると、一定時間内に終わらない子バッチを強制終了できます。
Qログファイルが肥大化してきました。どう対処しますか?
A日付付きのログファイル名(master_20240115.log など)を使い、バッチファイルでログローテーションを実装する方法で30日以上古いログを自動削除する処理を追加することを推奨します。マスタースクリプトの冒頭でログローテーションを呼ぶのが一般的なパターンです。
Q子バッチの数が多くなってきました。管理しやすい構成は?
A①フォルダで機能ごとに分類する(backup/report/maintenance/)、②設定ファイル(scripts_list.txt)で有効・無効を切り替えられるようにする(コメントアウト)、③用途別にマスタースクリプトを分けて、さらに上位のスクリプトで呼び出す階層構造にする、の3つが有効です。特に③の階層構造は「月次処理マスター」が「バックアップマスター」と「レポートマスター」を呼ぶ形で使われます。

まとめ

機能 実装方法 用途
順次実行 call + ERRORLEVEL チェック 依存関係のある処理の連続実行
動的管理 設定ファイル(scripts_list.txt) スクリプト追加・無効化をコード変更なしで
エラー中断 exit /b ERR DB更新など依存関係の強い処理
エラー集計 FAIL_COUNT + FAILED_LIST 独立した処理の集まり
リトライ 再帰 goto + RETRY_MAX 一時障害に強い処理
並列実行 start /b + 完了待ち 独立した処理の時間短縮
ドライラン --dry-run 引数 実行前の確認・デバッグ
再開機能 状態ファイル + --resume 障害後のやり直し効率化
ログ タイムスタンプ + 経過時間 トラブル時の原因特定
関連記事
CALL と START の違いを完全解説:CALL・START の動作の違いと使い分け。
バッチファイルで並列処理を実行する方法:並列処理の同時実行数制限と完了待ちの実装。
バッチファイルで実行時刻・経過時間をログに記録する完全ガイド:経過時間の計測とステップ記録の実装。
前回処理の状態を記録して次回に引き継ぐ方法:前回処理の状態を次回バッチに引き継ぐ方法。
バッチファイルでエラー通知メールを自動送信する完全ガイド:エラー発生時のメール・Slack 自動通知。