Linux监控文件变化首选inotify,基于内核事件驱动、无需轮询、低开销;常用inotifywait命令快速监听,需注意-m持续模式、-r递归限制、-e指定事件及内核参数调优,复杂逻辑应改用pyinotify。

inotify 是 Linux 下监控文件变化的首选方案,无需轮询、内核原生支持、开销低;其他方式(如 tail -F、auditd、自写轮询脚本)要么不精确,要么配置重、资源占用高,不建议作为默认选择。
用 inotifywait 快速监听单个文件或目录
这是最常用、上手最快的路径:装完 inotify-tools 就能直接跑命令验证逻辑。
- 必须加
-m才能持续监听,否则收到第一个事件就退出 - 监控目录时默认不递归,要加
-r;但递归会为每个子目录创建独立 watch,容易触发/proc/sys/fs/inotify/max_user_watches限制(默认仅 8192) - 显式指定事件类型,避免输出干扰项:比如只关心修改和新建,就用
-e modify,create,别裸用-e all或漏掉-e - 格式化输出更易解析:
--format '%w%f %e' --timefmt '%Y-%m-%d %H:%M:%S',其中%w是监控路径前缀,%f是相对文件名
示例:监听 /etc/hosts 修改并打印带时间戳的完整路径
inotifywait -m -e modify --format '%w%f %e %T' --timefmt '%Y-%m-%d %H:%M:%S' /etc/hosts
Shell 脚本中正确捕获 inotifywait 输出
用 | while read 看似顺手,但实际有严重陷阱:子 shell 中变量无法回传、信号处理失效,最关键的是——如果 while 循环体里执行耗时命令(如 rsync 或网络请求),下一条事件可能被丢弃(管道阻塞或 inotifywait 缓冲区满)。
- 健壮写法是让
inotifywait单次触发、执行完动作、再进下一轮循环 - 避免在
while read内部做长操作;如必须,加超时(timeout 30s cmd)或后台异步(cmd &)并忽略wait - 脚本退出前确保
inotifywait进程终止,否则残留 fd 可能影响后续监控 - 权限注意:监控
/etc/下文件需 root;普通用户只能监控自己有权限的路径
推荐写法(配置变更后 reload Nginx):
#!/bin/bash CONFIG="/etc/nginx/nginx.conf" while inotifywait -e modify "$CONFIG" >/dev/null; do nginx -t && systemctl reload nginx done
监控大规模目录前必须调优的三个内核参数
一旦递归监控成百上千子目录,大概率遇到 No space left on device 错误——这不是磁盘满了,而是触达了 inotify 的用户级资源上限。
-
/proc/sys/fs/inotify/max_user_watches:单用户可监控的文件/目录总数,默认 8192。监控/var/log这类深目录极易超限 -
/proc/sys/fs/inotify/max_user_instances:单用户可创建的 inotify 实例数,默认 128。一个inotifywait -m就占一个实例 -
/proc/sys/fs/inotify/max_queue_events:单个 inotify 实例的事件队列长度,默认 16384。高频小文件写入(如日志滚动)可能溢出
临时调大(以 max_user_watches=524288 为例):
echo 524288 > /proc/sys/fs/inotify/max_user_watches
永久生效需写入 /etc/sysctl.conf:
fs.inotify.max_user_watches = 524288
什么时候该换 pyinotify 而不是硬撑 shell
pyinotify 不是“高级版 inotifywait”,它是为复杂逻辑准备的编程接口。如果你的需求满足以下任一条件,shell 就不够用了:
- 需要路径白名单/黑名单(比如只响应
*.conf,跳过.swp或~备份文件) - 需要合并事件(如连续 5 次
modify合成一次通知,避免抖动) - 需要保持状态(比如记录上次处理的文件 inode,过滤重复事件)
- 要嵌入已有 Python 服务中,而非独立守护进程
简单判断:如果 inotifywait 加 grep/awk 还能应付,就别上 pyinotify;一旦开始写多层 if 判断路径、计数器、临时文件锁,说明该切到 Python 了。
真正容易被忽略的点是:inotify 监控的是 inode,不是路径名。符号链接目标变动不会触发源路径的事件;而如果监控的目录被 mv 或 rm -rf,watch 会自动失效且只发一次 IN_DELETE_SELF ——这常导致脚本静默停止,却没人检查 inotifywait 进程是否还活着。


















