inotify是linux下最直接、轻量且内核原生支持的文件监控方案;inotifywait适合快速验证和简单自动化,需加-m持续监听,-r递归时注意max_user_watches限制;shell中应避免while read管道阻塞,python中pyinotify适用于需路径过滤或状态保持的复杂场景;监控大规模目录前须调优三个内核参数。

Linux 下文件变动监控,inotify 是最直接、轻量且内核原生支持的方案;其他方式(如 tail -F、auditd、轮询)要么功能受限,要么开销高或配置重,不推荐作为默认选择。
inotifywait 命令行监听:快速验证和简单自动化
inotifywait 是调试和轻量任务的首选,装完就能用,无需写代码。它不是“监听后一直运行”的守护进程,而是事件触发即输出一行,靠 -m 参数实现持续模式。
- 必须加
-m才能持续监听,否则第一次事件就退出 - 监控目录时,默认不递归,要加
-r;但递归会为每个子目录创建独立 watch,容易触达/proc/sys/fs/inotify/max_user_watches限制(默认 8192) -
-e后可指定多个事件,用逗号分隔,例如-e modify,create,delete_self;避免不加-e导致输出大量IN_ACCESS或IN_OPEN干扰项 - 常用格式化组合:
--format '%w%f %e' --timefmt '%Y-%m-%d %H:%M:%S',其中%w是监控路径前缀,%f是相对事件文件名
示例:监控 /tmp/test 下新建文件并打印完整路径
inotifywait -m -e create --format '%w%f' /tmp/test
Shell 脚本中管道捕获事件:避免漏事件和死锁
用 | while read 接收 inotifywait 输出看似自然,但实际有陷阱:子 shell 中的变量无法回传、信号处理困难,更关键的是——如果 while 循环体里执行耗时命令(比如 rsync),下一条事件可能在等待中被丢弃(inotifywait 缓冲区满或管道阻塞)。
- 正确做法是把事件读取和动作分离:用
inotifywait单次触发,执行完再进下一轮while循环 - 避免在
while read内部做长操作;如必须,加超时或后台异步(&)并忽略wait - 注意权限:监控
/etc/下文件需 root;普通用户监控自己家目录则无问题 - 脚本退出前应确保
inotifywait进程终止,否则残留 fd 可能影响后续监控
健壮写法示例(配置变更后 reload):
#!/bin/bash CONFIG="/etc/nginx/nginx.conf" while inotifywait -e modify "$CONFIG" >/dev/null; do nginx -t && systemctl reload nginx done
Python 中用 pyinotify:需要路径过滤或状态保持时才上
pyinotify 不是“更高级的 inotifywait”,而是为复杂逻辑设计的编程接口。如果你只需要响应“某个文件改了”,shell 就够了;一旦涉及路径白名单、事件合并(如连续多次 modify 合成一次处理)、或需维护内存状态(比如记录上次修改时间),才值得引入。
-
pyinotify的WatchManager.add_watch()默认不递归,要显式传rec=True - 事件回调函数名必须严格匹配命名规范,如
process_IN_MODIFY,拼错就静默失效 - 它不自动处理子进程或线程安全,若回调中调用
subprocess.run,需自行控制并发(比如加锁或队列) - 相比
watchdog库,pyinotify更贴近 inotify 原语,但只支持 Linux;watchdog跨平台但底层可能降级为轮询
最小可用示例(仅响应 .conf 文件修改):
import pyinotify
class ConfHandler(pyinotify.ProcessEvent):
def process_IN_MODIFY(self, event):
if event.pathname.endswith('.conf'):
print(f"Config changed: {event.pathname}")
wm = pyinotify.WatchManager()
notifier = pyinotify.Notifier(wm, ConfHandler())
wm.add_watch('/etc/', pyinotify.IN_MODIFY, rec=True)
绕不开的三个内核参数:监控大规模目录前必查
当你监控 /var/log 或整个项目源码树时,大概率遇到 No space left on device 错误——这不是磁盘满了,而是 inotify 句柄耗尽。根源在三个 /proc/sys/fs/inotify/ 下的值,它们共同约束单个用户的监控能力。
-
max_user_watches:每个用户最多监控多少文件/目录。默认 8192,一个递归监控的目录可能轻松占掉上千个。扩容命令:echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches -
max_user_instances:每个用户最多创建几个 inotify 实例。inotifywait -m算一个实例,多个脚本并行就容易超。默认 128,一般够用 -
max_queue_events:每个实例的事件队列长度上限。默认 16384,事件爆发时(如批量touch)可能丢事件,但日常极少需要调 - 这些修改重启后失效,要永久生效需写入
/etc/sysctl.conf,如fs.inotify.max_user_watches=524288
真正容易被忽略的是:这些限制按 UID 计,不是按进程。同一个用户起 10 个 inotifywait,共用这三组配额。











