fsnotify/fsnotify
GitHub: fsnotify/fsnotify
一个 Go 语言跨平台文件系统事件通知库,将 Linux inotify、macOS/BSD kqueue、Windows ReadDirectoryChangesW 等系统级接口统一为一致的 Go API。
Stars: 10647 | Forks: 966
fsnotify 是一个 Go 库,用于在 Windows、Linux、macOS、BSD 和 illumos 上提供跨平台的文件系统通知。
需要 Go 1.23 或更高版本;完整文档位于
https://pkg.go.dev/github.com/fsnotify/fsnoop
平台支持:
| 后端 | 操作系统 | 状态 |
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
| inotify | Linux | 支持 |
| kqueue | BSD, macOS | 支持 |
| ReadDirectoryChangesW | Windows | 支持([不包括 `Chmod` 操作][#487]) |
| FEN | illumos | 支持 |
| fanotify | Linux 5.9+ | [尚未支持](https://github.com/fsnotify/fsnotify/issues/114) |
| FSEvents | macOS | [需要 x/sys/unix 的支持][fsevents] |
| USN Journals | Windows | [需要 x/sys/windows 的支持][usn] |
| Polling | *所有* | [尚未支持](https://github.com/fsnotify/fsnotify/issues/9) |
Linux 和 illumos 理论上应包含 Android 和 Solaris,但目前尚未经过测试。
## 用法
一个基本示例:
```
package main
import (
"log"
"github.com/fsnotify/fsnotify"
)
func main() {
// Create new watcher.
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
// Start listening for events.
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Has(fsnotify.Write) {
log.Println("modified file:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
// Add a path.
err = watcher.Add("/tmp")
if err != nil {
log.Fatal(err)
}
// Block main goroutine forever.
<-make(chan struct{})
}
```
可以在 [cmd/fsnotify](cmd/fsnotify) 中找到更多示例,可以通过以下命令运行:
```
% go run ./cmd/fsnotify
```
更详细的文档可以在 godoc 中找到:
https://pkg.go.dev/github.com/fsnotify/fsnotify
## 常见问题解答
### 当文件被移动到另一个目录时,它仍然会被监视吗?
不会,除非您正在监视它被移动到的目标位置。
### 会监视子目录吗?
不会,您必须为要监视的任何目录添加监视(递归监视器已在开发计划中:[#18])。
### 我必须在 goroutine 中监视 Error 和 Event channel 吗?
是的。您可以在同一个 goroutine 中使用 `select` 读取这两个 channel(您不需要为两个 channel 分别使用单独的 goroutine;请参阅示例)。
### 为什么通知在 NFS、SMB、FUSE、/proc 或 /sys 上不起作用?
fsnotify 需要底层操作系统的支持才能工作。当前的 NFS 和 SMB 协议不提供文件通知的网络级别支持,/proc 和 /sys 虚拟文件系统也不提供此支持。
这可以通过轮询监视器([#9])来解决,但尚未实现。
### 为什么我会收到很多 Chmod 事件?
某些程序可能会产生大量的属性更改;例如 macOS 上的 Spotlight、防病毒软件、备份应用程序以及其他一些已知会这样做的程序。通常情况下,最好忽略 Chmod 事件。它们通常没有用处,而且容易引起问题。
macOS 上的 Spotlight 索引可能会导致多个事件(参见 [#15])。一种临时的解决方法是将您的文件夹添加到 *Spotlight 隐私设置* 中,直到我们提供原生的 FSEvents 实现(参见 [#11])。
### 监视文件效果不佳
通常不建议监视单个文件(而不是目录),因为许多程序(尤其是编辑器)会以原子方式更新文件:它会写入一个临时文件,然后将其移动到目标位置,覆盖原始文件(或其变体)。此时,原始文件上的监视器会丢失,因为该文件已不复存在。
这样做的优点是,发生断电或崩溃时不会留下只写了一半的文件。
请监视父目录,并使用 `Event.Name` 过滤掉您不感兴趣的文件。在 `cmd/fsnotify/file.go` 中有一个这样的示例。
## 平台特定说明
### Linux
当文件被删除时,在所有文件描述符关闭之前不会发出 REMOVE 事件;它会发出一个 CHMOD 事件:
```
fp := os.Open("file")
os.Remove("file") // CHMOD
fp.Close() // REMOVE
```
这是 inotify 发送的事件,因此对此无法进行太多更改。
`fs.inotify.max_user_watches` sysctl 变量指定了每个用户的监视数量上限,而 `fs.inotify.max_user_instances` 指定了每个用户的 inotify 实例最大数量。您创建的每个 Watcher 都是一个“实例”,而您添加的每个路径都是一个“监视”。达到限制将导致“no space left on device”或“too many open files”错误。
这些参数也通过 `/proc` 路径暴露,即 `/proc/sys/fs/inotify/max_user_watches` 和 `/proc/sys/fs/inotify/max_user_instances`。默认值因发行版和可用内存而异。
要增加它们,您可以使用 `sysctl` 命令或将值写入 proc 文件:
```
sysctl fs.inotify.max_user_watches=200000
sysctl fs.inotify.max_user_instances=256
```
要使更改在重启后仍然生效,请编辑 `/etc/sysctl.conf` 或 `/usr/lib/sysctl.d/50-default.conf`(具体细节因 Linux 发行版而异;请查阅您发行版的文档):
```
fs.inotify.max_user_watches=200000
fs.inotify.max_user_instances=256
```
### Windows
目前尚未通过 fsnotify 的公共 API 启用递归监视(参见上面常见问题解答中的“会监视子目录吗?”)。以下说明描述了在内部启用递归监视时(例如,在 fsnotify 自身的测试中)观察到的 Windows 后端行为。将它们保留在此处是为了供遇到该行为的维护者和贡献者参考,因为递归代码路径仍然存在于后端中。
当启用递归监视并且您监视一个目录时,只要其内部的子条目被创建、重命名或删除,您就可能会收到该中间目录的 `Write` 事件。例如,对 `/a` 进行递归监视并创建一个新文件 `/a/b/c`,您将收到 `Create /a/b/c`,并且可能还会收到 `Write /a/b`。
发生这种情况是因为,在 NTFS 卷上,修改目录的条目会更新该目录的最后写入时间,而 Windows 后端请求了 `FILE_NOTIFY_CHANGE_LAST_WRITE` 以支持文件的 `Write` 事件。因此,相同的 `Write` 过滤器会捕获到目录的元数据更新。
kqueue 也有同样的“目录 `Write` = 目录内容已更改”语义,因此将目录上的 `Write` 视为“其内部的内容发生了变化”的可移植代码在 Windows 和 BSD/macOS 上可以正常工作,但在 Linux 上则不行(inotify 仅将 `Write` 用于文件内容的更改)。如果您只关心文件内容,请过滤掉路径指向目录的 `Write` 事件。
目录 `Write` 事件是否会与子事件一起实际交付并未得到保证:它取决于 `ReadDirectoryChangesW` 缓冲、NTFS 元数据更新时机以及事件合并机制,这些都不受 fsnotify 控制。
### kqueue (macOS,所有 BSD 系统)
kqueue 需要为每个被监视的文件打开一个文件描述符;因此,如果您正在监视一个包含五个文件的目录,那么就需要六个文件描述符。在这些平台上,您会更快地达到系统的“最大打开文件数”限制。
sysctl 变量 `kern.maxfiles` 和 `kern.maxfilesperproc` 可用于控制打开文件的最大数量。
标签:BSD, EVTX分析, FEN, fsnotify, Go, Golang, illumos, inotify, kqueue, ReadDirectoryChangesW, Ruby工具, SOC Prime, 事件监听, 事件驱动, 安全编程, 开发工具, 开源库, 搜索引擎爬虫, 操作系统, 数据挖掘, 文件变更通知, 文件监控, 文件系统监控, 日志审计, 目录监控, 系统编程