【bat】ファイルの存在を監視するバッチファイル完全ガイド|待機ループ・タイムアウト・出現/消滅検知・複数監視・実践パターンまで

「FTP転送が終わったら次の処理を動かしたい」「別バッチが出力するファイルが現れたら自動で取り込みたい」──こうした ファイル出現待ち・バッチ間連携 はシステム自動化の定番パターンです。

この記事では if existgoto ループ を使ったファイル存在監視を、タイムアウト・出現/消滅検知・複数ファイル対応・落とし穴対策まで段階的に解説します。

スポンサーリンク

目次

  1. 基本形:無限ループで存在を監視
  2. ファイル出現を待って自動処理する
  3. ファイル消滅を検知する
  4. タイムアウト付き待機
  5. 複数ファイルをまとめて監視
  6. バックグラウンドで監視する
  7. 落とし穴5選
  8. 実践例3本
  9. よくある質問(FAQ)
  10. まとめ

基本形:無限ループで存在を監視

if exist でファイルの存在を確認し、goto でループします。timeout /t でポーリング間隔を設けることで CPU 負荷をほぼゼロに抑えます。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\trigger.txt"  :: 監視するファイルのパス
set "INTERVAL=10"                       :: チェック間隔(秒)

:loop
if exist "%WATCH_FILE%" (
    echo [%time%] ファイルが存在します: %WATCH_FILE%
    :: ここにファイルが存在するときの処理を書く
) else (
    echo [%time%] ファイルなし。%INTERVAL%秒後に再確認...
)

timeout /t %INTERVAL% /nobreak > nul
goto :loop

timeout /t N /nobreak/nobreak が必須 です。省略すると任意のキー入力でスキップされ、意図しない早期ループになります(後述の落とし穴1参照)。

if exist の詳細な使い方(ワイルドカード・フォルダ対応など)は「【bat】IF EXISTでファイル・フォルダの存在確認をする方法」も参照してください。

ファイル出現を待って自動処理する

「ファイルが 現れたら」処理を実行するパターンです。ファイルが出現した瞬間に処理を走らせてループを抜けます。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\data\input.csv"
set "INTERVAL=5"

echo [%time%] 監視開始: %WATCH_FILE%

:wait_appear
if exist "%WATCH_FILE%" (
    echo [%time%] ファイル出現!処理を開始します
    goto :process
)
echo [%time%] 待機中...
timeout /t %INTERVAL% /nobreak > nul
goto :wait_appear

:process
echo ===== 処理開始 =====
:: ここに実際の処理を記述
echo %WATCH_FILE% を処理中...
:: 処理後にファイルを移動(再トリガー防止)
move "%WATCH_FILE%" "C:\work\done\input_%date:~0,4%%date:~5,2%%date:~8,2%.csv"
echo ===== 処理完了 =====

処理後にファイルを移動・削除する ことが重要です。そのままにすると次のループで再度処理されてしまいます。

ファイル消滅を検知する

「ファイルが 消えたら」処理を実行するパターンです。ロック中のファイルが解放されたタイミングを待つ用途などに使います。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\lock\process.lock"
set "INTERVAL=3"

echo [%time%] 消滅監視開始: %WATCH_FILE%

:wait_disappear
if not exist "%WATCH_FILE%" (
    echo [%time%] ファイルが消えました!
    goto :unlocked
)
echo [%time%] ファイルまだあり。%INTERVAL%秒後に再確認...
timeout /t %INTERVAL% /nobreak > nul
goto :wait_disappear

:unlocked
echo ロック解除を検知。後続処理を開始...
:: ここに後続処理を記述

ロックファイルを使ったバッチ間連携のパターンは「【bat】外部ファイルのフラグを読み込んで処理を分岐する方法」も参考にしてください。

タイムアウト付き待機

無限ループは実運用では危険です。最大待機時間(タイムアウト) を設けて、時間内にファイルが現れなければ警告を出して終了する実装にします。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\data\input.csv"
set "INTERVAL=10"       :: チェック間隔(秒)
set "MAX_WAIT=300"      :: 最大待機時間(秒)
set /a ELAPSED=0

echo [%time%] 監視開始(最大 %MAX_WAIT% 秒)

:wait_loop
if exist "%WATCH_FILE%" goto :found

set /a ELAPSED+=%INTERVAL%
if !ELAPSED! GEQ !MAX_WAIT! goto :timeout_reached

echo [%time%] 待機中... (%ELAPSED%秒経過 / 最大%MAX_WAIT%秒)
timeout /t %INTERVAL% /nobreak > nul
goto :wait_loop

:found
echo [%time%] ファイル出現確認!処理を開始します
:: 後続処理をここに記述
goto :end

:timeout_reached
echo [%time%] タイムアウト(%MAX_WAIT%秒待機しましたが、ファイルが出現しませんでした)
exit /b 1

:end
endlocal

exit /b 1 でタイムアウト時にエラーコード 1 を返すことで、呼び出し元のバッチで if errorlevel 1 によるエラー判定が可能になります。

タイムアウト・待機の基本については「【bat】バッチファイルで処理を待機させる方法」も参照してください。

複数ファイルをまとめて監視

すべてのファイルが揃うまで待つ(AND条件)

@echo off
setlocal enabledelayedexpansion

set "FILE1=C:\work\data\part1.csv"
set "FILE2=C:\work\data\part2.csv"
set "FILE3=C:\work\data\part3.csv"
set "INTERVAL=10"

:check_all
if not exist "%FILE1%" (
    echo [%time%] part1.csv 待機中...
    timeout /t %INTERVAL% /nobreak > nul
    goto :check_all
)
if not exist "%FILE2%" (
    echo [%time%] part2.csv 待機中...
    timeout /t %INTERVAL% /nobreak > nul
    goto :check_all
)
if not exist "%FILE3%" (
    echo [%time%] part3.csv 待機中...
    timeout /t %INTERVAL% /nobreak > nul
    goto :check_all
)

echo [%time%] 全ファイル揃いました!処理を開始します
:: ここに後続処理を記述

いずれか1つが出現したら動く(OR条件)

@echo off
setlocal enabledelayedexpansion

set "DIR=C:\work\data"
set "INTERVAL=5"

:check_any
:: いずれかの .csv ファイルが出現したら処理開始
for %%F in ("%DIR%\*.csv") do (
    echo [%time%] 検知: %%F
    goto :found_any
)
echo [%time%] 待機中...
timeout /t %INTERVAL% /nobreak > nul
goto :check_any

:found_any
echo 処理を開始します

バックグラウンドで監視する

start /b を使うと、監視バッチをバックグラウンドで起動し、メインバッチは別の処理を継続できます。

:: メインバッチ(main.bat)
@echo off

:: 監視バッチをバックグラウンドで起動
start /b "" cmd /c "watch.bat"

:: メインバッチは別の処理を継続
echo メイン処理を実行中...
timeout /t 30 /nobreak > nul
echo メイン処理完了

バックグラウンド実行時は監視バッチのログをファイルに書き出すと確認しやすくなります。
start /b "" cmd /c "watch.bat >> watch.log 2>&1"

落とし穴5選

落とし穴1:timeout の /nobreak を省略すると誤動作する

timeout /t 10 は任意のキー入力でスキップされます。コマンドプロンプト上でキーを押すと即座にループが進み、意図しない高速ポーリングになります。必ず /nobreak を付けるか、ping 127.0.0.1 -n N > nul を代替に使います。

:: NG:キー入力でスキップされる
timeout /t 10 > nul

:: OK:キー入力を無効化
timeout /t 10 /nobreak > nul

:: 代替:ping を使った待機(確実にN秒待つ)
ping 127.0.0.1 -n 11 > nul   :: -n は「秒数+1」で指定

落とし穴2:書き込み途中のファイルを if exist で誤検知する

if exist はファイルが 書き込み中(ロック中)でも true を返します。他プロセスがまだ書き込んでいる途中でもファイルが存在すると判定されるため、サイズチェックを組み合わせて安全に検知します。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\input.csv"

:check
if not exist "%WATCH_FILE%" (
    timeout /t 5 /nobreak > nul
    goto :check
)

:: ファイルサイズが安定するまで待つ
set /a PREV_SIZE=0
:size_check
for %%F in ("%WATCH_FILE%") do set /a CUR_SIZE=%%~zF
if !CUR_SIZE! NEQ !PREV_SIZE! (
    set /a PREV_SIZE=!CUR_SIZE!
    timeout /t 2 /nobreak > nul
    goto :size_check
)

echo [%time%] ファイルの書き込み完了を確認 (サイズ: !CUR_SIZE! bytes)
:: ここに処理を記述

落とし穴3:ネットワークドライブ上のファイルは if exist が遅い・不安定

UNCパス(\\server\share\file.txt)に対する if exist はネットワーク遅延の影響を受け、タイムアウトになる場合があります。ネットワークドライブ監視では間隔を長めに取るか(30秒以上)、dir コマンドとエラーレベルを組み合わせる方法が安定します。

落とし穴4:for ループ内で goto するとネストが壊れる

for ループの中から goto でラベルにジャンプするとループ状態が正しくクリアされない場合があります。複数ファイルのOR条件監視など、for ループで最初に見つかったファイルを処理したいときは、goto でラベルへ飛ぶ前に変数に記録するパターンが安全です。

:: 安全なパターン:for ループ内で変数にセットしてから goto
set "FOUND_FILE="
for %%F in ("%DIR%\*.csv") do (
    if "!FOUND_FILE!"=="" set "FOUND_FILE=%%F"
)
if "!FOUND_FILE!" NEQ "" goto :process_file

落とし穴5:監視対象のパスに日本語・スペースが含まれる

ファイルパスにスペースや日本語が含まれる場合、ダブルクォートで囲まないと if exist が正常に動作しないことがあります。変数に代入するときもパスはダブルクォートで囲む習慣を付けてください。

:: NG:スペース入りパスが分割される
if exist C:\work\My Documents\input.csv goto :found

:: OK:ダブルクォートで囲む
if exist "C:\work\My Documents\input.csv" goto :found

実践例3本

実践例1:FTPダウンロード完了後に自動取り込み

FTPクライアントがダウンロード完了後に作成する .done ファイルを待ち受けて、本体ファイルを自動処理します。

@echo off
setlocal enabledelayedexpansion

set "FTP_DIR=C:\ftp\incoming"
set "DONE_FLAG=%FTP_DIR%\download.done"
set "DATA_FILE=%FTP_DIR%\data.csv"
set "DEST_DIR=C:\work\import"
set "INTERVAL=15"
set "MAX_WAIT=3600"   :: 最大1時間待機
set /a ELAPSED=0

echo [%time%] FTPダウンロード完了フラグを待機中...

:wait_ftp
if exist "%DONE_FLAG%" goto :ftp_done
set /a ELAPSED+=%INTERVAL%
if !ELAPSED! GEQ !MAX_WAIT! (
    echo [%time%] タイムアウト。処理を中止します
    exit /b 1
)
echo [%time%] 待機中... (%ELAPSED%秒経過)
timeout /t %INTERVAL% /nobreak > nul
goto :wait_ftp

:ftp_done
echo [%time%] ダウンロード完了フラグを検知

:: データファイルの存在確認
if not exist "%DATA_FILE%" (
    echo [ERROR] データファイルが見つかりません: %DATA_FILE%
    exit /b 2
)

:: 取り込み処理
move "%DATA_FILE%" "%DEST_DIR%"
del "%DONE_FLAG%"
echo [%time%] 取り込み完了

実践例2:バッチ間連携フラグで工程管理

工程A(集計バッチ)が終わったら工程B(レポートバッチ)を動かす、という直列実行を フラグファイル で管理します。

:: step_a.bat(工程A)
@echo off
set "FLAG_DIR=C:\work\flags"
set "FLAG_RUNNING=%FLAG_DIR%\step_a.running"
set "FLAG_DONE=%FLAG_DIR%\step_a.done"

:: 開始フラグを立てる
echo %date% %time% > "%FLAG_RUNNING%"

:: 処理本体(ここに工程Aの処理を記述)
echo 工程A: 集計処理中...
timeout /t 5 /nobreak > nul

:: 完了フラグを立てて実行中フラグを削除
echo %date% %time% > "%FLAG_DONE%"
del "%FLAG_RUNNING%"
echo 工程A完了
:: step_b.bat(工程B)
@echo off
setlocal enabledelayedexpansion
set "FLAG_DIR=C:\work\flags"
set "FLAG_DONE=%FLAG_DIR%\step_a.done"
set "INTERVAL=10"
set "MAX_WAIT=1800"
set /a ELAPSED=0

:wait_step_a
if exist "%FLAG_DONE%" goto :step_a_finished
set /a ELAPSED+=%INTERVAL%
if !ELAPSED! GEQ !MAX_WAIT! (
    echo [ERROR] 工程Aの完了を待ちましたがタイムアウトしました
    exit /b 1
)
echo [%time%] 工程A完了待ち... (%ELAPSED%秒経過)
timeout /t %INTERVAL% /nobreak > nul
goto :wait_step_a

:step_a_finished
echo [%time%] 工程A完了を確認。工程Bを開始します
:: フラグを消費(次回実行時に再トリガーされないよう削除)
del "%FLAG_DONE%"
:: 工程Bの処理をここに記述

このようなフラグファイルを使った連携パターンの詳細は「【bat】外部ファイルのフラグを読み込んで処理を分岐する方法」に詳しく解説しています。

実践例3:ログファイル出現を検知してメール通知

エラーログファイルが出現(またはエラーが書き込まれた場合)に通知する監視デーモンパターンです。定期チェック+ログへの記録付き。

@echo off
setlocal enabledelayedexpansion

set "WATCH_FILE=C:\work\logs\error.log"
set "MONITOR_LOG=C:\work\logs\monitor.log"
set "INTERVAL=60"

call :log "監視開始 (間隔: %INTERVAL%秒)"

:monitor_loop
if exist "%WATCH_FILE%" (
    call :log "エラーログ検知: %WATCH_FILE%"
    :: アーカイブして通知(SMTP連携等はここに記述)
    set "ARCHIVE=C:\work\logs\error_%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%.log"
    set "ARCHIVE=!ARCHIVE: =0!"
    move "%WATCH_FILE%" "!ARCHIVE!" > nul
    call :log "アーカイブ完了: !ARCHIVE!"
) else (
    call :log "異常なし"
)
timeout /t %INTERVAL% /nobreak > nul
goto :monitor_loop

:log
echo [%date% %time%] %~1 >> "%MONITOR_LOG%"
echo [%date% %time%] %~1
goto :eof

ログファイルの内容(キーワード)まで監視したい場合は「【bat】ログファイルを監視して自動処理するバッチファイル完全ガイド」も参照してください。

よくある質問(FAQ)

Q. 監視を Ctrl+C 以外で安全に停止したい
A. 「停止フラグファイル」を使う方法が便利です。:loop の先頭で if exist "stop.flag" goto :eof をチェックし、stop.flag を作成するだけで監視を停止できます。タスクスケジューラや他のバッチから制御する場合にも有効です。
Q. 監視間隔を短くしたい(1秒未満)
A. timeout の最小値は1秒です。より細かい間隔が必要な場合は ping 127.0.0.1 -n 1 > nul(約10〜20ms)を繰り返すか、PowerShell の Start-Sleep -Milliseconds 500 を組み合わせます。ただし高頻度ポーリングはCPU負荷が上がるため、1秒以上の間隔が実運用では推奨です。
Q. ワイルドカードで「いずれかのファイルが出現したら」を検知したい
A. for %%F in ("C:\work\*.csv") do goto :found が使えます。for ループは最初にマッチしたファイルで goto に飛ぶため、OR条件の検知として機能します。上記「複数ファイル監視(OR条件)」のサンプルを参照してください。
Q. 監視中にバッチウィンドウを最小化したい
A. start /min cmd /k "watch.bat" で最小化状態で起動できます。バックグラウンド完全非表示には start /b か、VBScriptの WshShell.Run "watch.bat", 0(ウィンドウ非表示)の組み合わせが使えます。
Q. タイムアウト後にリトライしたい
A. タイムアウトのラベルから再度 set /a ELAPSED=0 でリセットして goto :wait_loop に戻せばリトライになります。リトライ回数の上限を別変数で管理し、上限を超えたら exit /b 1 で異常終了させる設計にすると堅牢です。リトライの詳細は「【bat】バッチファイルで処理を一時停止する方法完全ガイド」も参考にしてください。
Q. ファイルの存在確認と条件分岐について詳しく知りたい
A. if exist のワイルドカード対応・否定形・フォルダ確認など詳細は「【bat】バッチファイルでファイルの存在チェックと条件分岐を行う方法」を参照してください。

まとめ

バッチファイルでファイル存在を監視するポイントをまとめます。

  • 基本形if exist + goto loop + timeout /t N /nobreak の3点セット
  • 出現検知:ファイルが現れたら goto :found でループを抜ける
  • 消滅検知if not exist でファイルが消えるまで待機
  • タイムアウトELAPSED カウンターで最大待機時間を管理、exit /b 1 で異常終了
  • 書き込み中対策:ファイルサイズが安定するまで待ってから処理
  • /nobreaktimeout には必ず /nobreak を付ける

関連記事もあわせてご覧ください。