「アプリが数時間おきにフリーズするので自動で再起動したい」「監視カメラや業務ツールがクラッシュしたら即座に再起動させたい」という要件はバッチファイルで実現できます。定期再起動・異常終了検知型・複数アプリ同時管理・ログ記録など、現場でよく使われる実装パターンをすべて解説します。
- taskkill + start で定期的にアプリを再起動する基本パターン
- tasklist でプロセスの生死を確認して落ちたときだけ再起動する監視型
- 複数アプリを1つのバッチで同時管理する方法
- 再起動日時・回数をログファイルに記録する方法
- 管理者権限が必要なアプリを schtasks で安全に再起動する方法
- PowerShell の WaitForExit を使った確実なプロセス監視
自動再起動の実装方式比較
| 方式 | 仕組み | 再起動タイミング | ログ対応 | 実装難度 |
|---|---|---|---|---|
| 定期再起動(タイマー型) | timeout で待機後に taskkill→start | 一定時間ごと | △ | 低 |
| 監視型(tasklist) | ループでプロセス確認、落ちたら起動 | 異常終了を即検知 | ○ | 低 |
| 複数アプリ管理 | for ループで複数プロセスを一括確認 | 異常終了を即検知 | ○ | 中 |
| PowerShell WaitForExit | プロセス終了イベントを待って再起動 | 即時(CPU消費なし) | ◎ | 中 |
| schtasks 経由 | タスクスケジューラ経由で管理者実行 | 定期 or イベント駆動 | ◎ | 高 |
「定期的に強制再起動する」ならタイマー型がシンプルです。「クラッシュしたら即座に再起動する」なら監視型(tasklist)かPowerShell WaitForExitが確実です。管理者権限が必要なアプリには schtasks 経由での起動を組み合わせてください。
方法1:定期再起動の基本(タイマー型)
指定した間隔(秒)で対象アプリを終了→再起動するシンプルなループです。アプリが正常動作中でも強制再起動するため、データ保存が不要なビューア系・監視系アプリに向いています。
@echo off
setlocal
REM ===== 設定 =====
set APP_EXE=notepad.exe
set APP_PATH=C:\Windows\System32\notepad.exe
set INTERVAL_SEC=1800
REM ================
:RESTART_LOOP
echo [%DATE% %TIME%] アプリを再起動します: %APP_EXE%
REM 実行中なら終了
tasklist /FI "IMAGENAME eq %APP_EXE%" 2>nul | findstr /i "%APP_EXE%" >nul
if %ERRORLEVEL% equ 0 (
taskkill /im "%APP_EXE%" /f >nul 2>&1
timeout /t 3 /nobreak >nul
)
REM 起動
start "" "%APP_PATH%"
echo [%DATE% %TIME%] 起動完了。%INTERVAL_SEC%秒後に再起動します
timeout /t %INTERVAL_SEC% /nobreak >nul
goto RESTART_LOOP
対象プロセスが起動していない状態で
taskkill /im xxx.exe /f を実行すると「プロセスが見つかりませんでした」エラー(終了コード128)が返り、%ERRORLEVEL% が汚染されます。tasklist | findstr で存在確認してから taskkill する習慣をつけてください。@echo off REM 使い方: restart.bat [EXE名] [フルパス] [インターバル秒] REM 例: restart.bat notepad.exe "C:\Windows\notepad.exe" 3600 setlocal if "%~1"=="" ( echo 使い方: %~n0 [EXE名] [フルパス] [秒] & exit /b 1 ) set APP_EXE=%~1 set APP_PATH=%~2 set INTERVAL_SEC=%~3 if "%INTERVAL_SEC%"=="" set INTERVAL_SEC=3600 :LOOP echo [%TIME%] %APP_EXE% を再起動 taskkill /im "%APP_EXE%" /f >nul 2>&1 timeout /t 2 /nobreak >nul start "" "%APP_PATH%" timeout /t %INTERVAL_SEC% /nobreak >nul goto LOOP
方法2:プロセスを監視して異常終了時だけ再起動する(監視型)
定期再起動と違い、プロセスが生きている間は何もしないのが監視型の特徴です。アプリが正常終了したとき・クラッシュしたときだけ再起動します。ループ間隔(CHECK_INTERVAL)を短くするほど検知が速くなります。
@echo off
setlocal enabledelayedexpansion
REM ===== 設定 =====
set APP_EXE=myapp.exe
set APP_PATH=C:\apps\myapp.exe
set APP_ARGS=--config config.ini
set CHECK_INTERVAL=10
set RESTART_COUNT=0
set MAX_RESTART=20
set LOGFILE=C:\logs\restart.log
REM ================
if not exist "C:\logs" mkdir "C:\logs"
REM 初回起動
echo [%DATE% %TIME%] 初回起動: %APP_EXE% >> "%LOGFILE%"
start "" "%APP_PATH%" %APP_ARGS%
:MONITOR_LOOP
timeout /t %CHECK_INTERVAL% /nobreak >nul
REM プロセスの存在確認
tasklist /FI "IMAGENAME eq %APP_EXE%" 2>nul | findstr /i "%APP_EXE%" >nul
if %ERRORLEVEL% equ 0 goto MONITOR_LOOP
REM プロセスが落ちていた場合
set /a RESTART_COUNT+=1
echo [%DATE% %TIME%] !APP_EXE! 停止を検知。再起動 #!RESTART_COUNT! >> "%LOGFILE%"
if !RESTART_COUNT! gtr %MAX_RESTART% (
echo [%DATE% %TIME%] 再起動上限 %MAX_RESTART% 回に到達。監視を終了します >> "%LOGFILE%"
exit /b 1
)
start "" "%APP_PATH%" %APP_ARGS%
echo [%DATE% %TIME%] 再起動完了 #!RESTART_COUNT! >> "%LOGFILE%"
goto MONITOR_LOOP
アプリが起動直後にクラッシュし続ける場合、上限なしでは永久に再起動ループになります。
MAX_RESTART を設定しておくと「20回再起動しても改善しない」場合に監視自体を終了してアラートを出すなど次の対応につなげられます。再起動ループの詳細な設計はプロセスを監視して異常終了を検知する完全ガイドも参照してください。方法3:複数アプリを同時に監視・再起動する
業務システムでは複数のサービスを同時監視したいケースが多いです。for %%P in (...) で監視対象一覧をループして、各プロセスの状態を1つのバッチで管理できます。
@echo off
setlocal enabledelayedexpansion
REM 監視対象リスト(EXE名=起動コマンド の形式)
set APP1_EXE=appA.exe
set APP1_CMD=C:\apps\appA.exe --mode watch
set APP2_EXE=appB.exe
set APP2_CMD=C:\apps\appB.exe
set APP3_EXE=notepad.exe
set APP3_CMD=C:\Windows\notepad.exe
set CHECK_INTERVAL=15
set LOGFILE=C:\logs\multi_restart.log
if not exist "C:\logs" mkdir "C:\logs"
REM 初回起動
for /l %%I in (1,1,3) do (
call :start_app %%I
)
:MONITOR_LOOP
timeout /t %CHECK_INTERVAL% /nobreak >nul
for /l %%I in (1,1,3) do (
call :check_and_restart %%I
)
goto MONITOR_LOOP
:check_and_restart
set IDX=%1
call set EXE=%%APP!IDX!_EXE%%
call set CMD=%%APP!IDX!_CMD%%
tasklist /FI "IMAGENAME eq !EXE!" 2>nul | findstr /i "!EXE!" >nul
if %ERRORLEVEL% neq 0 (
echo [%DATE% %TIME%] !EXE! を再起動 >> "%LOGFILE%"
start "" !CMD!
)
goto :eof
:start_app
set IDX=%1
call set CMD=%%APP!IDX!_CMD%%
start "" !CMD!
goto :eof
監視対象が4件以上になる場合は、テキストファイルに EXE名,起動コマンド を列挙して
for /f %%L in (apps.txt) do ... で読み込む方式がメンテナンスしやすいです。こうすることで監視対象の追加・削除がバッチ本体を編集せずにできます。方法4:再起動ログを記録してタイムスタンプ・回数を管理する
再起動が頻発しているかどうかを把握するには、ログへの記録が欠かせません。日付別ファイルへの分割保存や、直近N件のみ保持するローテーションと組み合わせると長期運用でもディスクを圧迫しません。
@echo off
setlocal enabledelayedexpansion
set APP_EXE=myapp.exe
set APP_PATH=C:\apps\myapp.exe
set CHECK_INTERVAL=10
REM ログファイルは日付別
set LOGDIR=C:\logs\restart
if not exist "%LOGDIR%" mkdir "%LOGDIR%"
:GET_LOG
for /f "tokens=1-3 delims=/" %%A in ("%DATE%") do (
set LOG_DATE=%%A%%B%%C
)
set LOGFILE=%LOGDIR%\%LOG_DATE%.log
set RESTART_COUNT=0
set START_TIME=%TIME%
REM 初回起動
echo [%DATE% %TIME%] ===== 監視開始: %APP_EXE% ===== >> "%LOGFILE%"
start "" "%APP_PATH%"
:MONITOR
timeout /t %CHECK_INTERVAL% /nobreak >nul
REM 日付が変わったらログファイルを更新
for /f "tokens=1-3 delims=/" %%A in ("%DATE%") do (
set TODAY=%%A%%B%%C
)
if not "%LOG_DATE%"=="%TODAY%" goto GET_LOG
tasklist /FI "IMAGENAME eq %APP_EXE%" 2>nul | findstr /i "%APP_EXE%" >nul
if %ERRORLEVEL% equ 0 goto MONITOR
REM 再起動
set /a RESTART_COUNT+=1
echo [%DATE% %TIME%] [#!RESTART_COUNT!] %APP_EXE% 停止 → 再起動 >> "%LOGFILE%"
start "" "%APP_PATH%"
goto MONITOR
長期稼働するバッチでは日付が変わるとログファイル名を更新する必要があります。ループ内で
%DATE% と前回の日付を比較して、変わったタイミングで LOGFILE を更新するパターンが定番です。ログのタイムスタンプ設計の詳細はログを日付別ファイルに自動保存する完全ガイドも参照してください。@echo off
setlocal
set LOGFILE=C:\logs\restart.log
set MAX_SIZE=1048576
REM 1MB を超えたらローテーション
if exist "%LOGFILE%" (
for %%F in ("%LOGFILE%") do set FILE_SIZE=%%~zF
if !FILE_SIZE! gtr %MAX_SIZE% (
move /y "%LOGFILE%" "%LOGFILE%.bak" >nul
echo [%DATE% %TIME%] ログローテーション実施 >> "%LOGFILE%"
)
)
endlocal
方法5:管理者権限が必要なアプリを再起動する
システム系アプリや一部の業務ツールは管理者権限なしでは起動できません。バッチファイル自体を管理者として実行するか、schtasks で管理者権限のタスクを事前登録して呼び出す方法があります。
@echo off
setlocal
REM 管理者権限チェック
net session >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo 管理者権限が必要です。UAC で昇格を要求します...
powershell -Command "Start-Process cmd -ArgumentList '/c "%~f0"' -Verb RunAs"
exit /b
)
echo 管理者権限で実行中
:RESTART_LOOP
set APP_EXE=protected_app.exe
set APP_PATH=C:\apps\protected_app.exe
taskkill /im "%APP_EXE%" /f >nul 2>&1
timeout /t 3 /nobreak >nul
start "" "%APP_PATH%"
echo [%TIME%] 管理者権限で再起動しました
timeout /t 3600 /nobreak >nul
goto RESTART_LOOP
UACプロンプトを毎回表示させたくない場合は、あらかじめタスクスケジューラに「最高の特権で実行」のタスクを登録しておく方法が確実です。バッチから
schtasks /run /tn "タスク名" を呼び出すだけで管理者権限のプロセスを起動できます。管理者権限でのバッチ実行方法の詳細は管理者権限で自動実行するバッチの作り方も参照してください。@echo off
setlocal
REM 事前にタスクスケジューラに登録済みのタスク名
set TASK_NAME=MyAdminApp_Restart
set APP_EXE=protected_app.exe
:MONITOR_LOOP
timeout /t 10 /nobreak >nul
tasklist /FI "IMAGENAME eq %APP_EXE%" 2>nul | findstr /i "%APP_EXE%" >nul
if %ERRORLEVEL% neq 0 (
echo [%TIME%] %APP_EXE% 停止検知。タスク経由で再起動します
schtasks /run /tn "%TASK_NAME%"
timeout /t 5 /nobreak >nul
)
goto MONITOR_LOOP
方法6:PowerShell WaitForExit を使った確実なプロセス監視
バッチのループポーリングと違い、PowerShell の WaitForExit() はプロセス終了イベントを受け取るまでCPUを消費せずに待機します。監視間隔を短くしても負荷がかからないため、より即時性の高い再起動が実現できます。
@echo off
setlocal
set APP_PATH=C:\apps\myapp.exe
set APP_ARGS=--config config.ini
set MAX_RESTART=10
set LOGFILE=C:\logs\ps_restart.log
if not exist "C:\logs" mkdir "C:\logs"
powershell -NoProfile -ExecutionPolicy Bypass -Command "
$appPath = '%APP_PATH%'
$appArgs = '%APP_ARGS%'
$logFile = '%LOGFILE%'
$maxRestart = %MAX_RESTART%
$count = 0
while ($count -lt $maxRestart) {
$proc = Start-Process -FilePath $appPath -ArgumentList $appArgs -PassThru
$ts = Get-Date -Format 'yyyy/MM/dd HH:mm:ss'
Add-Content -Path $logFile -Value "[$ts] 起動 PID=$($proc.Id) #$count"
$proc.WaitForExit()
$exitCode = $proc.ExitCode
$ts = Get-Date -Format 'yyyy/MM/dd HH:mm:ss'
Add-Content -Path $logFile -Value "[$ts] 終了 ExitCode=$exitCode"
if ($exitCode -eq 0) {
Write-Host "[OK] 正常終了。再起動しません"
break
}
$count++
Start-Sleep -Seconds 3
}
if ($count -ge $maxRestart) {
Add-Content -Path $logFile -Value "[$(Get-Date -Format 'HH:mm:ss')] 再起動上限到達"
}
"
echo PowerShell 監視終了
ユーザーが意図的にアプリを閉じた場合(終了コード 0)まで再起動してしまうと困るケースがあります。
if ($exitCode -eq 0) { break } を入れておくと正常終了時には再起動せず、クラッシュ(非ゼロ終了コード)のときだけ再起動します。終了コードの扱いについてはエラー時に処理を中断・終了する方法も参照してください。powershell -NoProfile -ExecutionPolicy Bypass -Command "
$appPath = 'C:\apps\myapp.exe'
$logFile = 'C:\logs\ps_restart.log'
$quickCrashSec = 10
$count = 0
$maxRestart = 5
while ($count -lt $maxRestart) {
$startTime = Get-Date
$proc = Start-Process -FilePath $appPath -PassThru
$proc.WaitForExit()
$elapsed = (Get-Date) - $startTime
if ($elapsed.TotalSeconds -lt $quickCrashSec) {
$msg = "[$(Get-Date -Format 'HH:mm:ss')] 即死クラッシュ(${elapsed}s)。再起動 #$count"
} else {
$msg = "[$(Get-Date -Format 'HH:mm:ss')] 正常動作後終了(${elapsed}s)。再起動 #$count"
}
Add-Content -Path $logFile -Value $msg
$count++
Start-Sleep -Seconds 3
}
Add-Content -Path $logFile -Value '[終了] 再起動上限到達'
"
起動から N 秒以内に終了した場合は「即死クラッシュ」と判定してカウントを別管理したり、より緊急なアラートを出すことができます。長時間稼働後のクラッシュと起動直後クラッシュでは原因が異なるため、ログに経過時間を記録しておくと運用時の調査が格段に楽になります。
実運用のポイントと組み合わせ技
- ✅ 再起動上限: MAX_RESTART を設定してクラッシュ無限ループを防ぐ
- ✅ ログ記録: 再起動日時・回数・終了コードを必ず残す
- ✅ 終了コード判定: 正常終了(0)と異常終了を区別して再起動判断する
- ✅ 即死クラッシュ検出: 起動直後(10秒以内)の終了は特別扱いする
- ✅ 起動確認待機: taskkill 後に 2〜3 秒の猶予を置いてから start する
- ✅ 管理者権限の確認: net session で権限チェックをする
- ✅ PC起動時の自動実行: タスクスケジューラで監視バッチ自体を登録する
監視バッチ自体をPC起動時に自動実行させるにはPC起動・ログオン時に自動実行する完全ガイドの schtasks 登録方法が参考になります。プロセスの終了を待つ方法の全パターンはプロセスの終了を待つ全方法まとめを参照してください。
まとめ
- 定期再起動(タイマー型):
taskkill /im→start→timeoutのシンプルループ。存在確認を入れて安全に実装 - 監視型(tasklist): ループでプロセスの生死を確認して落ちたときだけ再起動。MAX_RESTART で無限ループを防ぐ
- 複数アプリ管理:
for /l+callでアプリ数を変数化して一括管理 - ログ記録: 日付別ファイル分割・サイズローテーション・再起動回数・終了コードを記録
- 管理者権限対応:
net sessionチェック + PowerShell UAC昇格、または schtasks 経由実行 - PowerShell WaitForExit: イベント駆動で CPU 負荷ゼロ、即死クラッシュ検出と終了コード判定も可能
関連記事: プロセスを監視して異常終了を検知する完全ガイド / タイムアウトを設定して自動終了する完全ガイド / ログを出力する方法完全ガイド
よくある質問(FAQ)
start は新しい独立したプロセスを起動します。元のウィンドウ位置・サイズを復元したい場合はアプリ側が設定を保存・復元する必要があります。バッチからウィンドウ位置を制御したい場合は start /min(最小化)や PowerShell の -WindowStyle オプションを使ってください。taskkill /f(強制終了)はプロセスをOSが強制的に終了するため、アプリ側の保存処理が走りません。ウィンドウにWM_CLOSEメッセージを送る taskkill /im xxx.exe(/f なし)を使うとアプリが正常終了シーケンスを実行します。ただし応答なし状態では効果がないため、タイムアウト後に /f にフォールバックする実装が安全です。tasklist /FI "WINDOWTITLE eq 監視バッチ" などで自分自身の2重起動を確認してください。CHECK_INTERVAL を短くすると CPU を使い続けます。WaitForExit はプロセス終了まで完全にブロック(CPU使用率ほぼ0%)するため、高頻度の監視が必要な場合や本番環境では PowerShell 方式が推奨です。プロセスの終了待ちの全パターンはプロセスの終了を待つ全方法まとめを参照してください。Send-MailMessage や Invoke-WebRequest(Slack Webhook)を呼び出してメール・チャット通知する方法があります。再起動を検知したタイミングで通知コードを挟むだけなので実装は簡単です。詳しくはエラーメール通知の組み込み方法を解説した記事も参照してください。

