fsnotify是一个用Go编写的文件系统通知库,可以监控文件系统中的文件和目录的变化,并在变化发生时通知应用程序。它支持跨平台,可以在Linux、macOS和Windows等不同的操作系统上运行。fsnotify利用了不同操作系统的文件系统通知机制,例如Linux上的inotify、macOS上的FSEvents以及Windows上的ReadDirectoryChangesW。
要求使用Go 1.17或更新版本;完整的文档请参见https://pkg.go.dev/github.com/fsnotify/fsnotify
不同操作系统支持情况:
后端 | 操作系统 | 状态 |
---|---|---|
inotify | Linux | 受支持 |
kqueue | BSD、macOS | 受支持 |
ReadDirectoryChangesW | Windows | 受支持 |
FEN | illumos | 受支持 |
fanotify | Linux 5.9+ | 暂未支持 |
AHAFS | AIX | aix分支;由于缺乏维护者和测试环境,属于实验性功能 |
FSEvents | macOS | 需要x/sys/unix支持 |
USN Journals | Windows | 需要x/sys/windows支持 |
轮询 | 所有 | 暂未支持 |
Linux和illumos应该包括Android和Solaris,但目前尚未进行测试。
应用场景
fsnotify的应用场景包括但不限于以下几种情况:
- 实时文件同步:fsnotify可以实时监控文件系统的变化,因此可以用于实现实时文件同步。当源文件发生变化时,可以立即将变化同步到目标文件,保证文件的一致性。
- 自动化构建:fsnotify可以监控项目的源代码和依赖文件的变化,从而在变化发生时触发构建命令,实现自动化构建。这样可以节省手动构建的时间和精力,提高开发效率。
- 文件备份:fsnotify可以监控需要备份的文件或目录的变化,在文件发生变化时立即备份。这样可以保证数据的安全性,避免因文件丢失或损坏而导致的数据损失。
- 实时日志监控:fsnotify可以监控日志文件的创建、修改和删除等操作,从而在日志文件发生变化时触发日志监控程序,实时监控日志内容的变化。
- 文件系统安全性监控:fsnotify可以监控文件系统的安全性事件,例如文件的访问、修改和删除等操作。这样可以实现对文件系统的安全性监控,及时发现并记录不安全的事件。
使用示例
一个基本示例:
package main
import (
"log"
"github.com/fsnotify/fsnotify"
)
func main() {
// 创建新的Watcher。
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
// 开始监听事件。
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("事件:", event)
if event.Has(fsnotify.Write) {
log.Println("修改的文件:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("错误:", err)
}
}
}()
// 添加需要监控的路径。
err = watcher.Add("/tmp")
if err != nil {
log.Fatal(err)
}
// 阻塞主goroutine。
<-make(chan struct{})
}
更多示例可以在cmd/fsnotify中找到,并可以使用以下命令运行:
% go run ./cmd/fsnotify
更详细的文档可以在godoc中找到:https://pkg.go.dev/github.com/fsnotify/fsnotify
常见问题解答
当文件移动到另一个目录时,它还会被监视吗?
不会,除非您正在监视它被移动到的位置。
是否监视子目录?
不会,您必须添加对要监视的每个目录的监视(递归监视器在计划中:#18)。
是否需要在goroutine中同时监视错误和事件通道?
是的。您可以使用select
在同一个goroutine中读取两个通道(您不需要为两个通道分别启动一个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都是一个“实例”,您添加的每个路径都是一个“watch”。
这些在/proc
中也可以找到,路径为/proc/sys/fs/inotify/max_user_watches
和/proc/sys/fs/inotify/max_user_instances
。
要增加它们,可以使用sysctl
或将值写入proc文件:
sysctl fs.inotify.max_user_watches=124983
sysctl fs.inotify.max_user_instances=128
要使更改在重启后生效,请编辑/etc/sysctl.conf
或/usr/lib/sysctl.d/50-default.conf
(每个Linux发行版的详细信息有所不同,请查阅您的发行版文档):
fs.inotify.max_user_watches=124983
fs.inotify.max_user_instances=128
达到限制将导致“设备上没有剩余空间”或“打开的文件太多”错误。
kqueue(macOS,所有BSD系统)
kqueue需要为每个被观察的文件打开一个文件描述符;因此,如果您正在观察一个包含五个文件的目录,那就是六个文件描述符。在这些平台上,您将更快地达到系统的“最大打开文件”限制。
可以使用sysctl变量kern.maxfiles
和kern.maxfilesperproc
来控制最大打开文件数。