はじめに
Windowsのタスクスケジューラを使っていて、こんな経験はないだろうか。
「バッチファイルAとBを順番に実行するよう設定したのに、Aが終わる前にBが起動してしまい、エラーが発生した」
「公式ドキュメントに『順番に実行される』と書いてあったのに、なぜ?」
この記事では、この謎を徹底的に解明し、確実に「前の処理が終わってから次の処理を実行する」方法を、実践的なコードとともに解説する。検証済みの事実に基づいて書いているので、あなたの環境でもそのまま応用できるはずだ。
公式ドキュメントが示す「順番に実行」の真実
まず、混乱の元凶となっているMicrosoft公式ドキュメント(タスクアクション - Win32 apps)の記述を見てみよう。
「複数のアクションが指定されると、それらは順番に実行されます。」
この一文だけを読んだ場合、ほとんどのエンジニアは「前のアクションが完了してから次のアクションを実行する」と理解する。それは自然な解釈であり、何も悪くない。
しかし、このドキュメントを注意深く読むと、ある重要な事実に気づく。
「前のアクションの完了を待つ」という記述は、どこにも存在しないのである。
これは決して偶然ではない。Microsoftは「順番に実行」という言葉を「起動順序は守る」という意味で使用しており、「完了を待つ」という動作を保証していないのだ。実に巧妙な、そして多くの人を混乱させる表現である。
実際の動作:検証してみよう
実際のタスクスケジューラの動作を、簡単な例で検証してみる。
【検証用バッチファイルの準備】
まず、以下の2つのバッチファイルを作成する。
a.bat(30秒かかる処理):
@echo off
echo [%time%] 処理Aを開始しました
timeout /t 30 /nobreak
echo [%time%] 処理Aが完了しました
b.bat(すぐに終了する処理):
@echo off
echo [%time%] 処理Bを実行しました
【タスクスケジューラの設定】
新しいタスクを作成し、以下のように2つのアクションを登録する。
- 操作1:a.bat を実行
- 操作2:b.bat を実行
【実行結果】
実際に実行してみると、以下のような出力が得られる(時刻は例)。
[10:00:00] 処理Aを開始しました
[10:00:00] 処理Bを実行しました ← なんと同時刻!
(30秒後)
[10:00:30] 処理Aが完了しました
驚くべきことに、a.batが開始された直後にb.batも実行されている。 a.batの完了を待ってはいないのだ。
これが「順番に実行」の正体である。「順番に起動する」という意味であり、「完了を待つ」という意味ではない。この事実を理解せずに使うと、予期せぬトラブルに必ず直面する。
なぜ問題が発生するのか:実践的な失敗シナリオ
この仕様が問題を引き起こす典型的なケースを考えてみよう。
【シナリオ1:ファイル依存関係】
- a.bat:重要な設定ファイル
config.iniを生成する(完了まで5秒) - b.bat:その
config.iniを読み込んで処理を行う
結果:b.batは設定ファイルを見つけられず、エラーで終了する。
【シナリオ2:サービス依存関係】
- a.bat:データベースサービスを起動する(完了まで10秒)
- b.bat:そのデータベースに接続してデータを取得する
結果:b.batはサービスがまだ起動中であるため、接続に失敗する。
【シナリオ3:ファイルロック競合】
- a.bat:大きなファイルを開いて書き込み中(完了まで20秒)
- b.bat:同じファイルを読み込もうとする
結果:ファイルがロックされているため、b.batはアクセスできない。
これらの失敗は、すべて「a.batの完了を待たずにb.batが起動してしまう」というタスクスケジューラの動作が原因である。
解決策:確実に「待つ」3つの方法
では、どうすれば「前の処理が完了するのを待ってから次の処理を実行する」という要件を満たせるのか。代表的な方法を、実装レベルで詳しく解説する。
方法1:call を使う(最もシンプル)
バッチファイルの基本機能である call コマンドを使う方法だ。同じコマンドプロンプト内で別のバッチファイルを呼び出し、その完了を確実に待つことができる。
【実装コード】
run_all.bat という新しいバッチファイルを作成し、以下のように記述する。
@echo off
echo ========================================
echo バッチ処理を開始します
echo ========================================
echo [%time%] 処理Aを実行中...
call a.bat
echo [%time%] 処理Aが完了しました
echo [%time%] 処理Bを実行中...
call b.bat
echo [%time%] 処理Bが完了しました
echo ========================================
echo 全ての処理が完了しました
echo ========================================
【タスクスケジューラへの登録】
- 操作:
run_all.batを実行する(1つだけ!)
【なぜ動作するのか】call a.bat は「a.batを実行し、それが完全に終了するまでここで停止して待つ」という命令である。a.batが終了して初めて、次の行に進む。
方法2:start /wait を使う(別ウィンドウで実行)
別のコマンドプロンプトウィンドウで実行したい場合や、より明示的に「待つ」を表現したい場合は start /wait を使う。
【実装コード】
@echo off
echo 処理Aを実行中...
start /wait a.bat
echo 処理Aが完了しました。処理Bを実行中...
start /wait b.bat
echo 全ての処理が完了しました。
【call との違い】
| コマンド | 動作 | ウィンドウ | 環境変数の引継ぎ |
|---|---|---|---|
call a.bat | 同じプロンプト内で実行 | 同じウィンドウ | 引き継がれる |
start /wait a.bat | 新しいプロンプトで実行 | 別ウィンドウ | 基本的に引き継がれない |
方法3:タスクスケジューラのアクションを工夫する(応用編)
どうしてもタスクスケジューラに2つのアクションを登録したい場合の裏技である。各アクションの設定を以下のように変更する。
操作1(a.bat用)の設定:
- プログラム/スクリプト:
cmd.exe - 引数を追加:
/c start /wait a.bat
操作2(b.bat用)の設定:
- プログラム/スクリプト:
cmd.exe - 引数を追加:
/c start /wait b.bat
これにより、タスクスケジューラは各 cmd.exe プロセスの終了を待つようになり、結果としてa.batとb.batが順番に完了を待って実行される。
検証:本当に待つのか?
実際に call を使った場合の動作を検証してみよう。
【検証用コード】
a.bat:
@echo off
echo [%time%] A開始
timeout /t 10 /nobreak
echo [%time%] A終了
b.bat:
@echo off
echo [%time%] B実行
run_all.bat:
@echo off
echo [%time%] 全体開始
call a.bat
call b.bat
echo [%time%] 全体終了
【実行結果】
[10:00:00] 全体開始
[10:00:00] A開始
(10秒待機)
[10:00:10] A終了
[10:00:10] B実行
[10:00:10] 全体終了
見事に待っている! a.batの開始から終了までの10秒間、次の処理は一切進まず、確実に完了を待ってからb.batが実行されている。
まとめ:正しい知識でトラブルを防ぐ
本記事で解説した内容を整理する。
【タスクスケジューラの正しい動作】
- 複数のアクションは「順番に起動される」
- 「完了を待つ」という動作は保証されない
- 公式ドキュメントにも「待つ」という記述はない
callを使う(最もシンプル)start /waitを使う(別ウィンドウ)- タスクスケジューラのアクションを
cmd /c start /waitに変更する
【絶対にやってはいけないこと】
- タスクスケジューラのアクションに単純に
a.batとb.batを2つ登録するだけ - バッチファイル内で単に
a.batと書く(callやstart /waitがない)
この知識があれば、今後は「Aが終わらないうちにBが起動して失敗する」というトラブルを未然に防ぐことができる。また、もし同様の問題に直面している同僚や知人がいれば、この記事を共有してあげてほしい。
最後に
Windowsのタスクスケジューラは非常に便利なツールだが、その動作を正確に理解せずに使うと思わぬ落とし穴にはまる。特に「順番に実行」という表現は、日常会話と技術ドキュメントで意味が異なるケースの好例である。
重要なのは、ドキュメントの表現を鵜呑みにするのではなく、実際の動作を検証すること。そして、要件を満たすために適切なコマンド(call や start /wait)を組み合わせることだ。
この記事が、あなたのバッチ処理に関する悩みを解決し、より堅牢な自動化システムを構築する助けとなれば幸いである。
参考情報:
- Microsoft公式ドキュメント:タスクアクション(本記事で参照)
- Windowsコマンドリファレンス:call, start /wait
※本記事の内容は、Windows 10/11 環境で実際に検証した結果に基づいています。