inotifywait 入門 - ファイル変更を監視して自動処理する
inotifywait とは何か?
結論:
inotifywaitは Linux の inotify カーネル機能を使い、ファイル作成・変更・削除などのイベントをポーリングなしで検知する CLI。変更をトリガーに自動処理を組める。
inotifywaitで ファイルシステムのイベント をリアルタイム検知できるwhile readループと組み合わせて 変更検知 → 自動処理 の型が作れるclose_writeとmax_user_watchesの 2 つの落とし穴 を避けられる
結論(実務の型)
- 1 回だけ待つ →
inotifywait path - 常時監視して処理を回す →
inotifywait -m -e close_write path - 「保存完了」を拾いたいなら
modifyではなくclose_write
前提(対象環境)
- OS:Ubuntu / Debian / RHEL 系などの Linux(Linux カーネル 2.6.13 以降の inotify)
- パッケージ
inotify-toolsが必要(後述) - ローカルファイルシステム前提(NFS など一部のネットワーク FS では検知できない)
なぜ inotifywait を使うのか?(ポーリングとの違い)
結論:
while sleep 1; do ...; doneのようなポーリングは CPU を無駄に使い検知も遅い。inotifywait はカーネルがイベントを push するため、低負荷かつ即時に反応する。
ファイル変更を検知する素朴な方法は「定期的に ls や stat を見に行く」ポーリングだ。だがこれには欠点がある。
- 間隔を短くすると CPU・I/O を無駄に消費する
- 間隔を長くすると検知が遅れる
- 監視対象が増えるほどスキャンコストが膨らむ
inotifywait はカーネルの inotify サブシステムにイベントを登録し、変更が起きた瞬間だけ通知を受け取る。アイドル時の負荷はほぼゼロで、反応は即時だ。watch コマンドが「定期的に画面を更新する」ポーリング型なのに対し、inotifywait は「イベント駆動」と理解するとよい。
inotifywait はどうやってインストールする?
結論:
inotifywaitは coreutils ではなくinotify-toolsパッケージに含まれる。Debian 系はapt、RHEL 系はdnfで導入する。
# Ubuntu / Debian $ sudo apt install inotify-tools # RHEL / Rocky / AlmaLinux(EPEL が必要な場合あり) $ sudo dnf install inotify-tools # Fedora $ sudo dnf install inotify-tools
インストールできたか確認する。
$ inotifywait --help | head -n 1
inotifywait 3.22.6.0
同梱の inotifywatch はイベントの統計を集計するツール。「どのファイルがどれだけアクセスされているか」を後から知りたいときに使う。本記事ではイベントを逐次処理する inotifywait を扱う。
基本の使い方:ファイル変更を監視する
結論: 引数にパスを渡すと、イベントが 1 回起きるまでブロックし、内容を表示して終了する。継続監視には
-m(monitor)を付ける。
1 回だけ待つ(ワンショット)
$ inotifywait /tmp/watchdir
別のターミナルで /tmp/watchdir 内のファイルを触ると、次のように出力して終了する。
Setting up watches. Watches established. /tmp/watchdir/ MODIFY test.txt
出力は 監視パス イベント名 ファイル名 の順。デフォルトではすべての種類のイベントを対象に、最初の 1 件で終了する。
常時監視する(-m)
毎回終了されては自動処理に使えない。-m(--monitor)を付けると終了せず、イベントを延々と出し続ける。
$ inotifywait -m /tmp/watchdir
Setting up watches. Watches established. /tmp/watchdir/ OPEN test.txt /tmp/watchdir/ MODIFY test.txt /tmp/watchdir/ CLOSE_WRITE,CLOSE test.txt
Setting up watches. などの起動ログは標準エラーに出る。スクリプトで処理したいときは -q(quiet)で抑制するとよい。
どのイベントを監視すべき?(-e の選び方)
結論:
-eで対象イベントを絞る。「ファイル保存の完了」を拾うならmodifyではなくclose_writeが正解。modify は書き込みのたびに何度も発火する。
-e(--event)を付けないと全イベントが対象になり、ノイズが多い。代表的なイベントは次のとおり。
| イベント | 発火タイミング |
|---|---|
create |
ファイル / ディレクトリが作成された |
modify |
内容が書き込まれた(書き込みごとに複数回) |
close_write |
書き込み用に開かれたファイルが閉じられた |
delete |
ファイル / ディレクトリが削除された |
moved_to |
このディレクトリへ移動 / リネームされて入ってきた |
moved_from |
このディレクトリから移動 / リネームで出ていった |
attrib |
権限・所有者・タイムスタンプなどが変わった |
複数指定は -e を並べるか、カンマ区切りで書く。
# 作成・書き込み完了・削除だけを監視 $ inotifywait -m -e create -e close_write -e delete /tmp/watchdir
modify ではなく close_write を使う理由
エディタやプログラムは 1 回の保存でも内部的に複数回書き込むことがあり、modify はそのたびに発火する。「保存が終わった」瞬間を 1 回だけ拾いたいなら、ファイルが閉じられたことを表す close_write が適切。これを知らないと「同じファイルで処理が何度も走る」事故になる。
出力を整形する:--format と --timefmt
結論:
--formatで出力を任意の書式に変えられる。%w監視パス・%fファイル名・%eイベント名・%Tタイムスタンプが基本。%Tは--timefmtと併用する。
デフォルト出力はスペース区切りで扱いにくい。--format でスクリプトが処理しやすい形に整える。
$ inotifywait -m --timefmt '%F %T' --format '%T | %e | %w%f' \
-e close_write /tmp/watchdir2026-06-05 21:30:11 | CLOSE_WRITE,CLOSE | /tmp/watchdir/report.csv
主なフォーマット指定子:
%w… 監視しているパス(watch 対象のディレクトリ)%f… イベント対象のファイル名(ディレクトリを監視している場合)%e… 発生したイベント名(カンマ区切り)%T… タイムスタンプ(--timefmtの strftime 書式で整形)
%w%f をつなげるとフルパスになる。区切り文字を変えたい場合は %Xe(X が区切り文字)も使える。
ディレクトリを再帰的に監視する(-r と watch 上限)
結論:
-rでサブディレクトリまで再帰監視できる。ただしディレクトリ 1 個につき watch を 1 個消費し、fs.inotify.max_user_watchesの上限に当たると失敗する。
$ inotifywait -m -r -e close_write /var/www
巨大なツリーを -r で監視すると、次のエラーが出ることがある。
Failed to watch /var/www; upper limit on inotify watches reached! Please increase the amount of inotify watches allowed per user via `/proc/sys/fs/inotify/max_user_watches'.
現在の上限を確認する。
$ cat /proc/sys/fs/inotify/max_user_watches
8192
一時的に引き上げる(再起動でリセット)。
$ sudo sysctl fs.inotify.max_user_watches=524288
恒久化するには設定ファイルに書く。
$ echo 'fs.inotify.max_user_watches=524288' | sudo tee /etc/sysctl.d/90-inotify.conf $ sudo sysctl --system
再帰監視のもう 1 つの注意点
inotify はディレクトリ単位で watch を張る。監視開始後に新しく作られた深い階層のサブディレクトリは、watch が張られるまでの一瞬の隙にイベントを取りこぼす可能性がある(レース)。頻繁にサブディレクトリが増えるツリーでは、この取りこぼしを前提に設計する。
実践:ファイル変更を検知して自動処理する
結論:
inotifywait -mの出力をwhile readで受け、ファイルごとに処理を回すのが定番の型。--formatで必要な値だけ渡すとパースが安定する。
監視ディレクトリに置かれた CSV を、書き込み完了のたびに処理する例。
#!/usr/bin/env bash
set -euo pipefail
WATCH_DIR=/var/spool/incoming
inotifywait -m -q \
--format '%w%f' \
-e close_write \
"$WATCH_DIR" |
while read -r filepath; do
case "$filepath" in
*.csv)
echo "処理開始: $filepath"
# ここで実際の処理(取り込み・変換・通知など)を行う
;;
*)
echo "対象外、スキップ: $filepath"
;;
esac
doneポイント:
-qで起動ログを抑制し、--format '%w%f'でフルパスだけを渡すclose_writeだけに絞り、「保存完了」のみを処理トリガーにするwhile read -rの-rでバックスラッシュをそのまま受ける
多重起動を防ぐ
この監視スクリプトを cron や systemd で起動する場合、二重に立ち上がると同じファイルを 2 回処理しかねない。flock でファイルロックを併用し、インスタンスを 1 つに限定すると安全。
エディタの「アトミック保存」に注意
vim などは「一時ファイルに書く → 元の名前へリネーム」という保存方式を取ることがある。この場合 close_write ではなく moved_to や create として観測される。テキストエディタの保存を確実に拾いたいなら、ディレクトリを監視して -e close_write -e moved_to の両方を対象にするとよい。
inotifywait のよくある質問
結論: 終了コードはイベント受信で 0、エラーで 1、
-tのタイムアウトで 2。NFS など一部のネットワーク FS では他ホストの変更を検知できない。
Q. 一定時間イベントが無ければ抜けたい
-t(--timeout)で秒数を指定する。タイムアウトで終了した場合の終了コードは 2。
$ inotifywait -t 30 -e close_write /tmp/watchdir
Q. 監視から特定のパスを除外したい
--exclude(大文字小文字を区別)/ --excludei(区別しない)に拡張正規表現を渡す。
$ inotifywait -m -r --exclude '\.git/' -e close_write /repo
Q. NFS 上のファイルを監視できる?
inotify はローカルカーネルが観測した変更だけを通知する。NFS など別ホストが書き換えるネットワーク FS では、その変更を検知できないことがある。共有ストレージの監視はポーリングなど別手段を検討する。