我想运行一个 cron 任务来删除.txt
超过 7 天的文件。我有两个命令。第一个命令:
/usr/bin/find /var/www/example.com/wp-content/targetdir -name "*.txt" -type f -mtime +7 -exec rm -f {} \;
第二条命令:
/usr/bin/find /var/www/example.com/wp-content/targetdir -name "*.txt" -type f -mtime +7 -delete
这两个命令都可用于删除.txt
7 天以上的文件。我读到第一个命令与race condition
.能否详细说明使用第二个命令相对于第一个命令的优点?使用其中一种的优点和缺点是什么?
答案1
我看到提到的比赛是这样的,来自斯特凡·查泽拉斯的评论另一个问题:
请注意,它具有(如果有的话)不存在的
-exec rm {} +
竞争条件漏洞。-delete
所以不要在其他人可写的目录上使用它。一些发现还可以-execdir
缓解这些漏洞。
我实际上并没有在那里(*)看到这一点,但我可以看到那里的竞争与如何-exec rm
传递文件的完整路径有关,导致在进程中再次遍历树rm
。
find
在遍历树并评估命令行上给出的条件后,rm
再次遍历树,并且它不知道给定的条件find
,因此现在不检查它们。使用-delete
和-execdir
,find
遍历树,进行给定的任何检查,然后删除文件,始终保持目录上的文件描述符打开。
有人可能会介入并在find
评估文件或目录的条件和rm
运行之间重命名文件或目录。所以,像这样:
- 根运行
find . -type f -user joe -exec rm -f {} \;
find
运行并找到目录./this/
,保存用户拥有的一些文件joe
- 另一个用户重命名
./this
并创建具有相同名称的符号链接,指向其他地方 find
运行rm -f ./this/hello.txt
,现在跟随符号链接。
hello.txt
现在可以由任何人拥有,而不仅仅是 user joe
,并且自从find
作为 启动以来root
,rm
也是如此,并且它很高兴地删除了符号链接另一端的文件。这是经典检查时间到使用时间 (TOCTTOU) 漏洞。
人们可能会举出更糟糕的例子,但这是一个普遍的想法。
使用-delete
或,这种情况不会发生,因为打开的-execdir
文件描述符仍然指向同一目录,即使该目录被重命名。运行时将其工作目录设置为该目录(系统调用通过文件描述符更改工作目录,同样不进行名称查找),同时类似地使用相对于包含目录的名称进行删除(使用)。find
./this
-execdir
rm
fchdir()
-delete
unlinkat()
请注意,默认情况下(即没有该-L
选项),find
不遵循遍历目录树时找到的符号链接。这在这里没有帮助,因为再次rm
只是传递给底层系统调用的路径,并且像往常一样遵循符号链接。这确实意味着被替换的目录(./this
上面)必须是被符号链接(或另一个目录)替换的实际目录,而不是被另一个符号链接替换的符号链接。
(*)写完这篇文章 20 分钟后,我注意到 Stéphane 的回答(下面链接)确实有描述相同种族问题的 GNU 手册的链接。 (叹。)
-delete
暗示-depth
和制造的问题-prune
与此无关,也不是竞争条件。这在为什么使用 -delete 选项查找会删除我的 /save/ 目录中的文件,而不使用 delete 选项查找却无法找到它们?GNU coreutilsfind
似乎已经获得了一条错误消息:
$ find a -name foo -prune -o -name hello.txt -delete
find: The -delete action automatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
另一个区别是-exec rm -f -- {} +
仅使用标准工具,而-delete
不是标准工具,即使在某种程度上得到普遍支持。例如FreeBSD和GNU支持它,但Busybox不支持。看:查找:“-exec rm {};”与“-delete”——为什么广泛推荐前者?在 superuser.com 上。