夜間バックアップ・定期レポート生成・システムメンテナンスなど、複数のバッチファイルを決まった順序で実行したい場面は多くあります。それぞれを個別に起動すると実行漏れやミスが起きやすく、エラーが発生しても気づかないまま後続処理が動いてしまうリスクがあります。
こうした問題を解決するのがマスタースクリプトです。複数のバッチファイルを「いつ・どの順で・どのパラメータで」実行するかを1か所で管理し、エラー時の制御・ログ記録・再実行まで一元化できます。
この記事では、単純な順次実行から始め、設定ファイルによる動的管理・並列実行・ドライランモード・失敗箇所からの再実行まで、実務レベルのパターンを体系的に解説します。CALL と START の違いを完全解説も合わせて読むと、CALL・STARTの動作の違いを深く理解できます。
CALL による順次実行の基本と注意点、設定ファイル(リスト)による実行スクリプト管理、エラー時の中断・スキップ・集計パターン、タイムスタンプ付きログと経過時間の記録、START による並列実行と完了待ち、ドライランモード(–dry-run)の実装、失敗したスクリプトだけ再実行する再開機能、本番運用に耐える完成版マスタースクリプト
マスタースクリプトとは何か
マスタースクリプトとは、複数のバッチファイルを呼び出す「指揮者」となる上位スクリプトです。各バッチファイル(子バッチ)は自分の処理だけに集中し、マスタースクリプトが実行順序・引数・エラー処理・ログ管理を一手に担います。
| 比較項目 | 個別実行(マスターなし) | マスタースクリプト |
|---|---|---|
| 実行漏れ | 手動確認が必要 | 自動化・漏れゼロ |
| 順序管理 | ドキュメントに依存 | スクリプト自体が仕様書 |
| エラー検知 | 見落としやすい | ERRORLEVEL で即時検知・記録 |
| ログ | 各バッチに散在 | 一元化して追跡しやすい |
| 再実行 | 全件やり直しか手動で対応 | 失敗箇所から再開できる |
| 変更コスト | 実行手順書も更新が必要 | スクリプト1か所だけ変更 |
基本構造:CALL による順次実行
最もシンプルな形は call で子バッチを順番に呼び出す方式です。call は子バッチの完了を待ってから次の処理に進むため、処理の順序が保証されます。CALL と START の違いを完全解説で詳しく解説していますが、start(非同期)との違いを把握しておくことが重要です。
@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 を省略して子バッチのファイル名を直接書くと(例: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
call の直後に set /a ERR=%ERRORLEVEL% で値を変数に保存してください。echo など別のコマンドを挟むと ERRORLEVEL が上書きされます。詳細はsetlocal enabledelayedexpansion 完全ガイドの「ERRORLEVEL保存」の項も参照してください。設定ファイル(リスト)による動的なスクリプト管理
実行するスクリプトをコード中に直書きすると、追加・削除のたびにマスタースクリプトを修正する必要があります。設定ファイルにスクリプトのリストを書き、マスタースクリプトはそれを読み込む方式にするとスクリプト本体を変更せずに実行内容を変えられます。
:: 先頭が :: の行はコメント(スキップされる) :: 書式: スクリプト名 [引数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
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
本番運用向け:完成版マスタースクリプト
これまでの要素をすべて統合した、実際の業務で使える完成形スクリプトです。ドライラン・設定ファイル管理・経過時間ログ・リトライ・再開機能・完了通知を含んでいます。
@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)
call で呼び出した子バッチが setlocal を使っていれば変数はローカルスコープです。値を引き継ぐには、①一時ファイルに書き込んで親で読み込む、②子バッチの exit /b の終了コードを使う(数値のみ)、③子バッチで setlocal を使わずに直接変数を設定する(非推奨)、の3つの方法があります。master_20240115.log など)を使い、バッチファイルでログローテーションを実装する方法で30日以上古いログを自動削除する処理を追加することを推奨します。マスタースクリプトの冒頭でログローテーションを呼ぶのが一般的なパターンです。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 自動通知。

