inotifywait 入門 - ファイル変更を監視して自動処理する

inotifywait 入門 - ファイル変更を監視して自動処理する

inotifywait とは何か?

結論: inotifywait は Linux の inotify カーネル機能を使い、ファイル作成・変更・削除などのイベントをポーリングなしで検知する CLI。変更をトリガーに自動処理を組める。

  • inotifywaitファイルシステムのイベント をリアルタイム検知できる
  • while read ループと組み合わせて 変更検知 → 自動処理 の型が作れる
  • close_writemax_user_watches2 つの落とし穴 を避けられる

結論(実務の型)

  • 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 するため、低負荷かつ即時に反応する。

ファイル変更を検知する素朴な方法は「定期的に lsstat を見に行く」ポーリングだ。だがこれには欠点がある。

  • 間隔を短くすると 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/watchdir
2026-06-05 21:30:11 | CLOSE_WRITE,CLOSE | /tmp/watchdir/report.csv

主なフォーマット指定子:

  • %w … 監視しているパス(watch 対象のディレクトリ)
  • %f … イベント対象のファイル名(ディレクトリを監視している場合)
  • %e … 発生したイベント名(カンマ区切り)
  • %T … タイムスタンプ(--timefmt の strftime 書式で整形)

%w%f をつなげるとフルパスになる。区切り文字を変えたい場合は %XeX が区切り文字)も使える。

ディレクトリを再帰的に監視する(-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_tocreate として観測される。テキストエディタの保存を確実に拾いたいなら、ディレクトリを監視して -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 では、その変更を検知できないことがある。共有ストレージの監視はポーリングなど別手段を検討する。

次に読む