【bat】バッチファイルで指定アプリを自動再起動する完全ガイド|定期再起動・異常終了検知・複数アプリ・ログ記録・管理者権限まで

【bat】バッチファイルで指定アプリを自動再起動する完全ガイド|定期再起動・異常終了検知・複数アプリ・ログ記録・管理者権限まで bat

「アプリが数時間おきにフリーズするので自動で再起動したい」「監視カメラや業務ツールがクラッシュしたら即座に再起動させたい」という要件はバッチファイルで実現できます。定期再起動・異常終了検知型・複数アプリ同時管理・ログ記録など、現場でよく使われる実装パターンをすべて解説します。

この記事でわかること

  • taskkill + start で定期的にアプリを再起動する基本パターン
  • tasklist でプロセスの生死を確認して落ちたときだけ再起動する監視型
  • 複数アプリを1つのバッチで同時管理する方法
  • 再起動日時・回数をログファイルに記録する方法
  • 管理者権限が必要なアプリを schtasks で安全に再起動する方法
  • PowerShell の WaitForExit を使った確実なプロセス監視
スポンサーリンク

自動再起動の実装方式比較

方式 仕組み 再起動タイミング ログ対応 実装難度
定期再起動(タイマー型) timeout で待機後に taskkill→start 一定時間ごと
監視型(tasklist) ループでプロセス確認、落ちたら起動 異常終了を即検知
複数アプリ管理 for ループで複数プロセスを一括確認 異常終了を即検知
PowerShell WaitForExit プロセス終了イベントを待って再起動 即時(CPU消費なし)
schtasks 経由 タスクスケジューラ経由で管理者実行 定期 or イベント駆動
方式の使い分け
「定期的に強制再起動する」ならタイマー型がシンプルです。「クラッシュしたら即座に再起動する」なら監視型(tasklist)PowerShell WaitForExitが確実です。管理者権限が必要なアプリには schtasks 経由での起動を組み合わせてください。

方法1:定期再起動の基本(タイマー型)

指定した間隔(秒)で対象アプリを終了→再起動するシンプルなループです。アプリが正常動作中でも強制再起動するため、データ保存が不要なビューア系・監視系アプリに向いています。

restart_periodic.bat: 30分ごとに定期再起動する基本パターン
@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 前に存在確認をする理由
対象プロセスが起動していない状態で taskkill /im xxx.exe /f を実行すると「プロセスが見つかりませんでした」エラー(終了コード128)が返り、%ERRORLEVEL% が汚染されます。tasklist | findstr で存在確認してから taskkill する習慣をつけてください。
引数でEXE名・パス・インターバルを受け取る汎用版
@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)を短くするほど検知が速くなります。

monitor_restart.bat: プロセス落ちを検知して即再起動する監視ループ
@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)を設ける理由
アプリが起動直後にクラッシュし続ける場合、上限なしでは永久に再起動ループになります。MAX_RESTART を設定しておくと「20回再起動しても改善しない」場合に監視自体を終了してアラートを出すなど次の対応につなげられます。再起動ループの詳細な設計はプロセスを監視して異常終了を検知する完全ガイドも参照してください。

方法3:複数アプリを同時に監視・再起動する

業務システムでは複数のサービスを同時監視したいケースが多いです。for %%P in (...) で監視対象一覧をループして、各プロセスの状態を1つのバッチで管理できます。

multi_restart.bat: 複数アプリを一括監視して落ちたものだけ再起動
@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件のみ保持するローテーションと組み合わせると長期運用でもディスクを圧迫しません。

restart_with_log.bat: タイムスタンプ・再起動回数・経過時間を記録する
@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 で管理者権限のタスクを事前登録して呼び出す方法があります。

管理者権限チェック付き再起動バッチ(UAC自己昇格)
@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
schtasks を使った管理者権限実行の登録
UACプロンプトを毎回表示させたくない場合は、あらかじめタスクスケジューラに「最高の特権で実行」のタスクを登録しておく方法が確実です。バッチから schtasks /run /tn "タスク名" を呼び出すだけで管理者権限のプロセスを起動できます。管理者権限でのバッチ実行方法の詳細は管理者権限で自動実行するバッチの作り方も参照してください。
schtasks 経由でタスク登録済みの管理者アプリを再起動する
@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を消費せずに待機します。監視間隔を短くしても負荷がかからないため、より即時性の高い再起動が実現できます。

PowerShell WaitForExit で終了イベントを受け取って即再起動(バッチから呼び出し)
@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 監視終了
正常終了(ExitCode=0)では再起動しない設計
ユーザーが意図的にアプリを閉じた場合(終了コード 0)まで再起動してしまうと困るケースがあります。if ($exitCode -eq 0) { break } を入れておくと正常終了時には再起動せず、クラッシュ(非ゼロ終了コード)のときだけ再起動します。終了コードの扱いについてはエラー時に処理を中断・終了する方法も参照してください。
起動後N秒以内にクラッシュした場合は「即死ループ」と判定して停止する
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 /imstarttimeout のシンプルループ。存在確認を入れて安全に実装
  • 監視型(tasklist): ループでプロセスの生死を確認して落ちたときだけ再起動。MAX_RESTART で無限ループを防ぐ
  • 複数アプリ管理: for /l + call でアプリ数を変数化して一括管理
  • ログ記録: 日付別ファイル分割・サイズローテーション・再起動回数・終了コードを記録
  • 管理者権限対応: net session チェック + PowerShell UAC昇格、または schtasks 経由実行
  • PowerShell WaitForExit: イベント駆動で CPU 負荷ゼロ、即死クラッシュ検出と終了コード判定も可能

関連記事: プロセスを監視して異常終了を検知する完全ガイド / タイムアウトを設定して自動終了する完全ガイド / ログを出力する方法完全ガイド

よくある質問(FAQ)

Qtaskkill で終了させても start で再起動しても同じウィンドウに戻りません。
Astart は新しい独立したプロセスを起動します。元のウィンドウ位置・サイズを復元したい場合はアプリ側が設定を保存・復元する必要があります。バッチからウィンドウ位置を制御したい場合は start /min(最小化)や PowerShell の -WindowStyle オプションを使ってください。
Qアプリが終了するときにデータを保存してから終了してほしいのですが taskkill /f では保存されません。
Ataskkill /f(強制終了)はプロセスをOSが強制的に終了するため、アプリ側の保存処理が走りません。ウィンドウにWM_CLOSEメッセージを送る taskkill /im xxx.exe(/f なし)を使うとアプリが正常終了シーケンスを実行します。ただし応答なし状態では効果がないため、タイムアウト後に /f にフォールバックする実装が安全です。
Q再起動バッチ自体がフリーズした場合はどうすればよいですか?
A監視バッチ自体をタスクスケジューラで「毎時1回起動」のトリガーで登録しておくと、バッチがフリーズしても次の起動タイミングで新しいインスタンスが立ち上がります。複数インスタンスの重複起動を防ぐには、ループ先頭で tasklist /FI "WINDOWTITLE eq 監視バッチ" などで自分自身の2重起動を確認してください。
QPowerShell WaitForExit と tasklist ポーリングの使い分けは?
Aバッチのポーリング方式は実装がシンプルで、bat の知識だけで完結します。ただし CHECK_INTERVAL を短くすると CPU を使い続けます。WaitForExit はプロセス終了まで完全にブロック(CPU使用率ほぼ0%)するため、高頻度の監視が必要な場合や本番環境では PowerShell 方式が推奨です。プロセスの終了待ちの全パターンはプロセスの終了を待つ全方法まとめを参照してください。
Q再起動ループが動いていることをメールや通知で知らせる方法はありますか?
Aバッチから PowerShell の Send-MailMessageInvoke-WebRequest(Slack Webhook)を呼び出してメール・チャット通知する方法があります。再起動を検知したタイミングで通知コードを挟むだけなので実装は簡単です。詳しくはエラーメール通知の組み込み方法を解説した記事も参照してください。