如何在Linux中监视完整的目录树以检测更改?

109

如何在Linux(ext3文件系统)中监视整个目录树的变化?

目录中大约有30,000个子目录,组织在三个目录级别中,包含大约一百万个文件

这些文件大多数很小(<1KB,有些少于100KB)。它们构成了一个队列,我需要在文件创建、删除或修改内容后的5-10秒内得到通知。

我知道有inotify等工具,但据我所知它们只能监视单个目录,这意味着我需要在我的情况下使用3,000个inotify句柄,超过了单个进程允许的通常的1024个句柄限制。我是否错了?

如果Linux系统无法满足我的需求:也许有一个FUSE项目可以模拟一个文件系统(复制真实文件系统上所有文件访问),并单独记录所有修改(找不到这样的项目)?

11个回答

104

我曾经使用inotifywait工具做过类似的事情:

#!/bin/bash
while true; do

inotifywait -e modify,create,delete -r /path/to/your/dir && \
<some command to execute when a file event is recorded>

done

这将在整个目录树上设置递归目录监视,并允许您在某些内容更改时执行命令。如果您只想查看更改,可以添加-m标志以将其置于监视模式。


14
为避免使用“while循环”,请使用“-m”或“--monitor”开关/选项/标记/参数。不知道这个“开关”什么时候出现的,但比循环更好。 - gwillie
4
你还应该添加 move 事件:inotifywait -e modify,create,delete,move -r /path/to/your/dir - famzah
2
这种方法在两个事件在一瞬间发生的情况下会错过一个事件,不是吗?在inotifywait退出后,会有一个时期没有监视任何事件,对吧? - Grief
1
@gwillie,但如果使用了m标志,它只会输出到stdout,我们无法使用该触发器执行任何命令。因此,如果您想在观察到任何事件后执行某些操作,那么while循环是否更好? - NewHere

51
$ inotifywait -m -r /path/to/your/directory
这个命令足以递归监视目录的所有事件,例如访问、打开、创建、删除等。

3
是的,但像访问和打开这样的事件非常棘手,这取决于你的意图。例如:我想在www目录中每次有变化时重新启动cordova run。结果,由cordova生成的open、access事件会触发inotifywait,进入无限循环。对于大多数用途来说,使用“-e modify,create,delete,move”更好。 - Hatoru Hansou

30
据我所知,除了递归地在每个目录上设置一个inotify观察器外,没有其他方法。
也就是说,您不会因为inotify不必保留fd以监视文件或目录(其前身dnotify确实受到此限制)而用尽文件描述符。相反,inotify使用“观察描述符”。
根据inotifywatch的文档, 默认限制为8192个watch描述符,并且可以通过将新值写入/proc/sys/fs/inotify/max_user_watches来增加它。

听起来不错。在使用这么多的监视描述符时需要考虑哪些负面因素? - Udo G
不,除了创建所有的手表所需的时间外,我认为你不会遇到仅有3000个子目录的问题。 - Frédéric Hamidi
这难道不会引起潜在的竞争问题吗?例如,在folder_main中创建folder_sub,在folder_sub中创建folder_sub_sub,当folder_main的inotify到达时,设置了对folder_sub的监视,但是已经错过了folder_sub_sub,因此它上面也没有安装监视器? - Koen G.
2
Ubuntu 18.04现在默认将'max_user_watches'设置为65536,这在普通桌面/服务器系统中似乎是一个合理的值。 - Lothar
@KoenG。是的,这是丢失某些“事件”的可能性之一,但你可以实现“最终一致性”,即你会知道“自上次内存图片以来发生了1个或多个更改”。然后,在main/folder_sub/folder_sub_sub上安装一个监视器并读取其当前文件。到那时,有可能创建、更改和删除了某个文件,而你甚至都不知道。 考虑将inotify作为优化的“触发器重新扫描”,而不是“完整历史记录”;如果你必须拥有完整的历史记录,可以选择类似FUSE或strace的东西。 - Beni Cherniavsky-Paskin

23

如果你有很多子目录,inotify是最好的选项,但如果没有,我习惯使用以下命令:

watch -d find <<path>>


手表是首选。 - qodeninja
3
watch 命令不支持分页,因此它将丢失比终端高度更长的任何内容(例如,文件数大于终端行数的 tree 命令)。 - Nick Bull
3
我很想看看支持每5-10秒在500,000个文件上执行“查找”操作的硬件(以及该方法对其工作负载产生的影响)。如果我是你的系统管理员,并看到你创建这种类型的负载,我会追踪你并给你一个非常严肃的警告。 - tink
1
@tink 如果你有很多文件需要检查,多次运行 find 并不是最好的方法。我的回答适用于想要检查子目录但没有访问 inotify 的人们。正如我所建议的,当你有很多文件时,inotify 是最好的选择。 - fmassica

13

使用inotify-tools中的inotifywait:

sudo apt install inotify-tools

现在创建一个名为myscript.sh的脚本,其中包括隐藏的文件和文件夹:

#!/bin/bash
while true; do

inotifywait -e modify,create,delete,move -r $1

done

使用命令 chmod +x myscript.sh 使脚本可执行。

使用命令 ./myscript.sh /folder/to/monitor 运行它。

如果你不提供参数,它将默认使用工作目录。

此外,您可以在前一个命令的末尾添加 && \ 来运行多个命令:

#!/bin/bash
while true; do

inotifywait -e modify,create,delete,move -r $1 && \
echo "event" && \
echo "event 2"

done

如果您不想在事件上执行任何命令,只需使用-m修饰符直接运行命令,这样就不会关闭:

inotifywait -e modify,create,delete,move -m -r /path/to/your/dir


8

我有一个不同的建议,只针对文件的更改并记录历史更改。

使用Git。

cd /folder_to_monitor
git init
git add *
git commit -m "first snapshot"

所以在您进行更改后

git diff

3
在某些情况下,这可能是一个有效的选项。不应该被评为-1。 - Greg Woods

4

fanotify最终是否提供了这种功能?

引用LWN的话:

fanotify有两种基本的“模式”,即定向模式和全局模式。[...] fanotify全局模式表示它想要系统上的所有内容,然后单独标记它不关心的索引节点。

我不确定它的最新状态是什么。


1
根据对https://dev59.com/THI-5IYBdhLWcg3weoPR#1847268的评论... fanotify在2.6.36版本中被引入。 - Chris J

2

我遇到了同样的问题,我的程序创建了一些以点号开头的文件,我想要手动检查它们的内容,但这些文件很快又被自动删除了。

使用inotify在循环中没有使用监视选项对我来说不起作用,因为它太慢并且会错过事件,所以我想出了这个脚本:

target="$1"

cd "$target"
mkdir backup/

inotifywait -e modify,create,delete --monitor -r --include "\..*" "$target" | \
while read line
do
  echo "$line"
  if [[ "$line" == "$target CREATE "* ]] || [[ "$line" == "$target MODIFY "* ]]
  then
    filename=${line#"$target CREATE "}
    filename=${filename#"$target MODIFY "}
    cp --verbose "$filename" backup/
  fi
done

1
特别是对于大型或复杂的监控任务,您希望基于所见内容触发事件,请查看 Watchman文件监视服务。以下是一个简单的示例,每当更改CSS文件时运行名为minify-css的工具:
$ watchman watch ~/src
$ watchman -- trigger ~/src buildme '*.css' -- minify-css

它可以进行全面的日志记录,可以有效地处理在目录结构中重叠的多个监视器,可以通过命令行或json进行管理,还有更多功能。请参见

它可以通过Debian Sid和Ubuntu 20.04获得,并且从我所看到的情况来看,已经两次接近进入Fedora(14505901564720)。


-1
使用watch命令非常简单,它是一个原生的Linux命令(几乎安装在每个Linux发行版上)。
watch -n <interval> <command> <path>

这将每200毫秒显示/tmp/test目录的更改。
watch -n 0.2 ls -la /tmp/test

对于递归使用,您可以像下面这样使用watch

 watch -n 0.2 find <path>

这个不起作用。如果我运行命令 touch /tmp/test/x,它会执行,但如果我运行命令 touch /tmp/test/subdir/y,它就不会执行。问题明确要求一个递归解决方案。 - undefined
是的,你说得对,我们可以使用watch命令和find来实现那个功能。 - undefined
现在你只是在重复半个十年前的答案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接