【bat】バッチファイルでプロセスの終了を待つ全方法まとめ|start /wait・tasklist・timeout・waitfor

【bat】バッチファイルでプロセスの終了を待つ全方法まとめ|start /wait・tasklist・timeout・waitfor bat

バッチファイルで外部プログラムを実行した後、「終了するまで待ってから次の処理に進みたい」場面は頻繁にあります。インストーラの完了後に設定ファイルを配置する、ビルド完了後にテストを走らせる、といったケースです。

この記事では、start /waittasklist 監視timeoutwaitfor など、バッチファイルで使えるプロセス待機の全方法を比較し、場面ごとの使い分けを解説します。

スポンサーリンク

待機方法の全体比較

まず、それぞれの方法の特徴を整理します。

方法 待機の対象 ERRORLEVEL 最適な場面
直接実行 プロセス終了 取得可 CUI アプリ・bat の順次実行
start /wait プロセス終了 取得可 GUI アプリ・インストーラの待機
tasklist 監視 プロセス終了 取得不可 start で起動済みのプロセスを後から監視
timeout 指定秒数 固定時間の待機
waitfor シグナル受信 バッチ間・PC間の連携

「プログラムの終了を待つ」なら start /wait、「起動済みプロセスを監視する」なら tasklist、「一定時間だけ待つ」なら timeout が最適です。

方法1:start /wait(最も基本かつ確実)

start /wait は、プログラムが終了するまでバッチの実行を一時停止します。

基本の使い方

@echo off
echo メモ帳を起動します...
start "" /wait notepad.exe
echo メモ帳を閉じました。次の処理に進みます。
pause

メモ帳を閉じるまで「メモ帳を閉じました」は表示されません。

ERRORLEVEL で終了コードを取得する

start /wait の最大の利点は、プログラムの終了コードを取得できることです。

@echo off
start "" /wait "C:MyAppsetup.exe" /S
if %ERRORLEVEL% equ 0 (
    echo インストール成功
) else if %ERRORLEVEL% equ 3010 (
    echo インストール成功(要再起動)
) else (
    echo インストール失敗(コード: %ERRORLEVEL%)
    exit /b %ERRORLEVEL%
)

注意ERRORLEVEL直後に確認してください。間に echo 等を挟むと値がリセットされます。

パスにスペースが含まれる場合の注意

start コマンドは、最初のダブルクォーテーションをウィンドウタイトルとして解釈します。

rem NG: パスがタイトルとして解釈され、アプリが起動しない
start /wait "C:Program FilesMyAppapp.exe"

rem OK: 空のタイトル "" を先に指定する
start "" /wait "C:Program FilesMyAppapp.exe"

start コマンドでは常に最初に "" を書く習慣にすると安全です。

start /wait が効かないケース

start /wait でもプログラムの終了を待てないケースがあります。

ケース 原因 対処法
Microsoft Store アプリ ランチャーがすぐ終了し、本体は別プロセスで起動する PowerShell の Start-Process -Wait を使う
自己解凍型 exe 解凍後に別プロセスでインストーラを起動する msiexec で直接 .msi を指定する
ランチャー経由のアプリ ランチャーが終了しても本体が残る tasklist で本体プロセスを監視する
タスクスケジューラ経由 セッション分離で待機が効かない場合がある 直接実行(start を使わない)に変更する

Store アプリ等で start /wait が効かない場合は、バッチ内から PowerShell を呼びます。

rem PowerShell の Start-Process -Wait で待機する
powershell -command "Start-Process 'notepad' -Wait"

方法2:直接実行と call(同期実行)

exe を直接書くだけでも、そのプログラムが終了するまでバッチは待機します。

@echo off
echo ビルド開始...
"C:MyAppuild.exe" --config release
echo ビルド完了(ERRORLEVEL: %ERRORLEVEL%)

echo テスト開始...
"C:MyApp	est.exe" --all
echo テスト完了(ERRORLEVEL: %ERRORLEVEL%)

call はバッチ→バッチ専用

別のバッチファイルを呼ぶ場合は call が必須です。call なしだと呼び出し元に戻りません。

@echo off
rem bat → bat は call 必須
call build.bat
echo build.bat が終了しました(ERRORLEVEL: %ERRORLEVEL%)

rem bat → exe は call 不要(直接実行で待機する)
"C:MyAppdeploy.exe"
echo deploy.exe が終了しました

直接実行 vs start /wait の使い分け

項目 直接実行 start /wait
ウィンドウ 同じコマンドプロンプトで実行 新しいウィンドウで実行
最小化 / 最大化 不可 /min /max で指定可
作業ディレクトリ カレントディレクトリ /d で指定可
プロセス優先度 変更不可 /low /high 等で指定可
主な用途 CUI アプリ・bat GUI アプリ・インストーラ

CUI アプリなら直接実行、GUI アプリで起動オプションを指定したい場合は start /wait が適しています。

方法3:tasklist でプロセスの終了を監視する

start(/wait なし)で起動したプロセスや、既に起動済みのプロセスの終了を待つには、tasklist でポーリング監視します。

@echo off
echo Excel を起動して終了を待ちます...
start "" "C:Program FilesMicrosoft Office
ootOffice16EXCEL.EXE"

:WAIT_LOOP
timeout /t 2 /nobreak >nul
tasklist /fi "imagename eq EXCEL.EXE" 2>nul | find "EXCEL.EXE" >nul
if %ERRORLEVEL% equ 0 goto WAIT_LOOP

echo Excel が終了しました。次の処理に進みます。

仕組み

  1. tasklist で指定プロセスの存在を確認
  2. find が見つかれば ERRORLEVEL=0 → まだ動いているのでループ
  3. find が見つからなければ ERRORLEVEL=1 → 終了したのでループ脱出

複数プロセスの完了を待つ

並列で起動した全プロセスが終了するまで待つパターンです。

@echo off
echo 3つのタスクを並列実行します...
start "" "C:MyApp	ask1.exe"
start "" "C:MyApp	ask2.exe"
start "" "C:MyApp	ask3.exe"

:WAIT_ALL
timeout /t 2 /nobreak >nul
tasklist /fi "imagename eq task1.exe" 2>nul | find "task1.exe" >nul && goto WAIT_ALL
tasklist /fi "imagename eq task2.exe" 2>nul | find "task2.exe" >nul && goto WAIT_ALL
tasklist /fi "imagename eq task3.exe" 2>nul | find "task3.exe" >nul && goto WAIT_ALL

echo 全タスクが終了しました

いずれか1つでもまだ動いていれば && goto WAIT_ALL でループに戻ります。

タイムアウト付きの監視

プロセスが固まった場合に備えて、タイムアウトを設けることもできます。

@echo off
setlocal enabledelayedexpansion
set TIMEOUT_SEC=120
set ELAPSED=0

start "" "C:MyAppheavy_task.exe"

:WAIT_TIMEOUT
timeout /t 5 /nobreak >nul
set /a ELAPSED+=5

tasklist /fi "imagename eq heavy_task.exe" 2>nul | find "heavy_task.exe" >nul
if !ERRORLEVEL! neq 0 (
    echo 処理が完了しました(%ELAPSED%秒)
    goto :DONE
)

if !ELAPSED! geq %TIMEOUT_SEC% (
    echo タイムアウト(%TIMEOUT_SEC%秒): プロセスを強制終了します
    taskkill /f /im heavy_task.exe >nul 2>&1
    goto :DONE
)
goto WAIT_TIMEOUT

:DONE
endlocal

注意:ループ内で ERRORLEVEL を正しく取得するには、setlocal enabledelayedexpansion!ERRORLEVEL!! で囲む形式)を使う必要があります。%ERRORLEVEL% はループ突入時の値のまま更新されません。

方法4:timeout で指定時間だけ待機する

timeout はプロセスの終了を検知するのではなく、指定した秒数だけ一時停止するコマンドです。

@echo off
echo サービスを再起動します...
net stop MyService
timeout /t 5 /nobreak >nul
net start MyService
echo サービスを再起動しました

timeout の主要オプション

オプション 意味
/t 秒数 待機する秒数(0〜99999) timeout /t 10
/nobreak キー入力でスキップ不可にする timeout /t 10 /nobreak
/t -1 キーが押されるまで無制限に待機 timeout /t -1

> nul を付けると「あと○秒」のカウントダウン表示を抑制できます。

timeout が使えない場合の代替手段

timeout は Windows Vista 以降のコマンドです。また、タスクスケジューラ経由で実行した場合に正しく動作しないことがあります。

代替1:ping コマンド

rem 10秒待機(ping 11回 = 約10秒)
ping -n 11 localhost >nul

ping は1秒間隔で送信するため、待ちたい秒数 + 1-n に指定します。

代替2:choice コマンド

rem 10秒待機
choice /n /t 10 /d Y >nul

代替3:waitfor コマンド

rem 10秒待機(存在しないシグナルを待つことで時間稼ぎ)
waitfor DummySignal /t 10 >nul 2>&1

方法5:waitfor でシグナルを待つ

waitfor は、別のバッチファイルや別のPCから送られるシグナルを待機するコマンドです。

基本の使い方

待機側と送信側の2つのバッチで連携します。

待機側(wait.bat)

@echo off
echo コピー完了シグナルを待っています...
waitfor CopyDone /t 300
if %ERRORLEVEL% equ 0 (
    echo シグナルを受信しました。後続処理を開始します。
) else (
    echo タイムアウトしました(300秒)
    exit /b 1
)

送信側(send.bat)

@echo off
echo ファイルコピー中...
xcopy /s /e "C:Source*" "D:Dest" >nul
echo コピー完了。シグナルを送信します。
waitfor /si CopyDone

/t でタイムアウト秒数を指定できます。省略すると無制限に待機します。

waitfor の制約

  • シグナル名は英数字のみ(日本語不可)
  • 同時に待機できるシグナルは1つだけ
  • ネットワーク経由の場合は同じドメイン内のPCのみ送受信可能

サイレントインストーラの待機

キッティング(PCセットアップ自動化)で最も多い用途が、インストーラの完了待ちです。

msiexec の場合

rem MSI パッケージのサイレントインストール
start "" /wait msiexec /i "C:Installersapp.msi" /quiet /norestart
echo msiexec 終了コード: %ERRORLEVEL%

exe インストーラの場合

サイレントオプションはインストーラの種類によって異なります。

インストーラ種類 サイレントオプション 実行例
NSIS /S start "" /wait setup.exe /S
Inno Setup /VERYSILENT start "" /wait setup.exe /VERYSILENT
InstallShield /s /v/qn start "" /wait setup.exe /s /v/qn

msiexec の主要エラーコード

コード 意味
0 正常終了
1602 ユーザーがキャンセル
1603 致命的なエラー
1618 他のインストールが実行中
3010 成功(再起動が必要)

実践テンプレート

テンプレート1:ビルド → テスト → デプロイの順次実行

@echo off
setlocal
cd /d "%~dp0"

echo === Step 1: ビルド ===
"C:Toolsuild.exe" --config release
if %ERRORLEVEL% neq 0 (
    echo [エラー] ビルドに失敗しました(コード: %ERRORLEVEL%)
    exit /b 1
)

echo === Step 2: テスト ===
"C:Tools	est.exe" --all
if %ERRORLEVEL% neq 0 (
    echo [エラー] テストに失敗しました(コード: %ERRORLEVEL%)
    exit /b 2
)

echo === Step 3: デプロイ ===
start "" /wait "C:Toolsdeploy.exe" --target production
if %ERRORLEVEL% neq 0 (
    echo [エラー] デプロイに失敗しました(コード: %ERRORLEVEL%)
    exit /b 3
)

echo === 全ステップ完了 ===
endlocal
exit /b 0

テンプレート2:キッティング用の連続インストール

@echo off
setlocal
cd /d "%~dp0"
set LOG=%~dp0install.log

echo [%date% %time%] キッティング開始 >> "%LOG%"

rem --- Chrome ---
echo Chrome をインストール中...
start "" /wait msiexec /i "%~dp0installersGoogleChrome.msi" /quiet /norestart
call :CHECK_RESULT "Chrome" %ERRORLEVEL%

rem --- 7-Zip ---
echo 7-Zip をインストール中...
start "" /wait "%~dp0installers7z-setup.exe" /S
call :CHECK_RESULT "7-Zip" %ERRORLEVEL%

rem --- VS Code ---
echo VS Code をインストール中...
start "" /wait "%~dp0installersVSCodeSetup.exe" /VERYSILENT /NORESTART
call :CHECK_RESULT "VS Code" %ERRORLEVEL%

echo [%date% %time%] キッティング完了 >> "%LOG%"
echo.
echo ===========================
echo   全ソフトのインストール完了
echo ===========================
pause
endlocal
exit /b 0

:CHECK_RESULT
if %2 equ 0 (
    echo   → %~1 : 成功
    echo [%date% %time%] %~1 : 成功(コード: %2) >> "%LOG%"
) else if %2 equ 3010 (
    echo   → %~1 : 成功(要再起動)
    echo [%date% %time%] %~1 : 成功-要再起動(コード: %2) >> "%LOG%"
) else (
    echo   → %~1 : 失敗(コード: %2)
    echo [%date% %time%] %~1 : 失敗(コード: %2) >> "%LOG%"
)
exit /b 0

テンプレート3:並列実行して全完了を待つ(タイムアウト付き)

@echo off
setlocal enabledelayedexpansion
cd /d "%~dp0"

set MAX_WAIT=300
set INTERVAL=5
set ELAPSED=0

echo 3つの処理を並列実行します...
start "" "C:MyAppprocess_a.exe"
start "" "C:MyAppprocess_b.exe"
start "" "C:MyAppprocess_c.exe"

:POLL
timeout /t %INTERVAL% /nobreak >nul
set /a ELAPSED+=%INTERVAL%

set RUNNING=0
for %%P in (process_a.exe process_b.exe process_c.exe) do (
    tasklist /fi "imagename eq %%P" 2>nul | find "%%P" >nul
    if !ERRORLEVEL! equ 0 set RUNNING=1
)

if !RUNNING! equ 1 (
    if !ELAPSED! geq %MAX_WAIT% (
        echo [警告] タイムアウト(%MAX_WAIT%秒)
        taskkill /f /im process_a.exe >nul 2>&1
        taskkill /f /im process_b.exe >nul 2>&1
        taskkill /f /im process_c.exe >nul 2>&1
        exit /b 1
    )
    goto POLL
)

echo 全処理が完了しました(%ELAPSED%秒)
endlocal
exit /b 0

まとめ

やりたいこと 方法
exe / インストーラの終了を待つ start "" /wait app.exe
bat から別の bat を呼んで待つ call sub.bat
起動済みプロセスの終了を待つ tasklist + ループ監視
並列実行して全完了を待つ start 複数 + tasklist 監視
固定秒数だけ待機する timeout /t 秒数 /nobreak
別のバッチからの合図を待つ waitfor シグナル名
start /wait が効かないアプリ PowerShell Start-Process -Wait

ポイント

  • start /wait が最も基本的で確実。パスを指定するときは先頭に空タイトル "" を付ける
  • start /wait 直後に %ERRORLEVEL% を確認すればプログラムの成否を判定できる
  • ループ内で ERRORLEVEL を使う場合は enabledelayedexpansion!ERRORLEVEL! が必須
  • timeout はプロセス終了の検知ではなく時間待機。タスクスケジューラ経由では動作しないことがある
  • タスクスケジューラや古い環境では ping -n 秒数+1 localhost >nul で代替できる

あわせて読みたい