Watchdog监听文件系统事件易漏触发或重复触发,因底层OS机制不保证“写完才通知”;应优先响应on_moved(临时文件重命名)、on_created后延时检查文件大小稳定,避免直接读取未完成内容。

Watchdog 能可靠监听文件系统事件,但默认行为容易漏掉快速连续写入、符号链接或临时文件干扰——关键在事件过滤和延迟处理。
为什么 FileSystemEventHandler 会漏触发或重复触发?
Watchdog 的底层依赖操作系统 inotify(Linux)、FSEvents(macOS)或 ReadDirectoryChangesW(Windows),这些机制本身不保证“文件写完才通知”。比如编辑器保存时先写临时文件再重命名,或大文件分块写入,都会导致 on_modified 多次触发、on_created 和 on_moved 顺序混乱。
- 用
on_modified直接处理文件内容,大概率读到不完整数据;应优先响应on_moved(尤其是 from_ 为临时路径时)或on_created后加time.sleep(0.1)再检查文件大小是否稳定 -
os.path.islink(path)在on_created中可能返回False,因为符号链接创建和目标文件就绪存在微小时间差;若需排除软链,建议在实际处理前再次验证 - 避免在事件回调里做耗时操作(如解析大 JSON、调用外部命令),否则会阻塞事件队列;改用
threading.Thread或asyncio.to_thread()(Python 3.9+)异步执行
如何只监听特定后缀且忽略编辑器临时文件?
Watchdog 本身不内置文件名过滤,必须手动判断 event.src_path。重点不是“怎么写正则”,而是避开常见陷阱:VS Code 的 .filename.tmp、Sublime 的 filename.swp、以及 macOS 的 .DS_Store 都该跳过,且注意大小写(Windows 不敏感,Linux/macOS 敏感)。
- 推荐用
pathlib.Path(event.src_path).suffix.lower() in {'.py', '.txt', '.log'}判断后缀,比str.endswith()更安全(能正确处理.tar.gz等多点后缀) - 用
if any(event.src_path.endswith(suffix) for suffix in ('.swp', '.tmp', '.~')):过滤临时文件,不要用in检查子串(例如'config.tmp.bak'会被误判) - 如果监听目录下有大量无关文件(如
node_modules),用ignore_patterns=['*/node_modules/*', '*/__pycache__/*']初始化Observer,比事后过滤更省资源
start() 后程序立即退出?必须用 join() 或信号捕获
Watchdog 的 Observer 在后台开新线程运行,主线程执行完就结束,导致监听瞬间失效。这不是 bug,是设计使然——你需要主动维持主进程存活。
- 最简方案:
observer.start(); try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop(); observer.join() - 生产环境建议用
signal.signal(signal.SIGINT, lambda s, f: observer.stop())+observer.join(),避免 Ctrl+C 后残留线程 - 若集成进 Flask/FastAPI 服务,别在
app.run()前直接observer.start();改用atexit.register(observer.stop)并确保observer.join(timeout=1)在应用关闭时被调用
真正难的不是启动监听,而是区分“文件已就绪可处理”和“只是系统抖动”。多试几次 touch test.txt && echo hello > test.txt,再对比编辑器保存行为,你会立刻明白为什么 sleep 0.1 和检查 mtime/st_size 组合比单纯等事件更靠谱。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











