我想删除一个目录中的旧文件,该目录在多个子目录中包含大量文件。
我正在尝试使用以下方法 - 经过一番谷歌搜索后,这似乎是推荐且有效的方法:
find . -mindepth 2 -mtime +5 -print -delete
我的期望是,这应该打印一个满足条件的文件(修改时间超过 5 天并满足 mindepth 条件),然后将其删除,然后继续处理下一个文件。
但是,当此命令运行时,我可以看到查找的内存使用量正在增加,但没有打印任何内容(因此我认为尚未删除任何内容)。这似乎意味着find
首先收集所有满足条件的文件,在遍历整个文件系统树后,它将打印然后删除文件。
有没有办法让它在对文件运行测试后立即删除它?这将有助于逐步进行清理 - 我可以选择终止该命令,然后稍后重新运行它(这将有效地恢复文件删除)。目前这似乎不会发生,因为 find 直到完成遍历巨大的文件系统树才开始删除任何内容。有没有办法解决?
编辑 - 包括有关我的用例的请求数据:
我要清理的目录的最大深度约为 4;常规文件仅存在于文件系统的叶子中。大约有 6 亿个常规文件,叶目录最多包含 5 个文件。较低层的目录扇出约为 3。较高层的扇出很大。单个 7.2 TB LVM 磁盘占用的总空间为 6.5 TB(具有 4 个约 2 TB 物理 HDD)
答案1
find命令慢的原因
那真是一个有趣的问题...或者,老实说,恶意的:
命令
find . -mindepth 2 -mtime +5 -print -delete
与通常的试用版本非常不同,省略了危险部分-delete
:
find . -mindepth 2 -mtime +5 -print
棘手的部分是动作-delete
暗示选项-depth
。包含删除的命令确实是
find . -depth -mindepth 2 -mtime +5 -print -delete
并且应该进行测试
find . -depth -mindepth 2 -mtime +5 -print
这与你所看到的症状密切相关;该选项-depth
正在更改树遍历文件系统树的算法预序深度优先搜索到一个中序深度优先搜索。
以前,到达的每个文件或目录都会立即使用,然后被遗忘。寻找是利用树本身来寻找道路。find
现在需要收集可能包含仍待找到的文件或目录的所有目录,然后再删除最深的目录优先。为此,它需要自己完成规划和记住遍历步骤的工作,并且 - 这就是重点 - 以与文件系统树自然支持的顺序不同的顺序。因此,实际上,在第一步输出工作之前,它需要收集多个文件的数据。
Find 必须跟踪一些目录以便稍后访问,这对于少数目录来说不是问题。
但也许有很多目录,对于很多不同程度的目录。
此外,在这种情况下,find 之外的性能问题也会变得明显;所以有可能并不是find
那么慢,而是其他原因。
它对性能和内存的影响取决于您的目录结构等。
相关章节来自man find
:
请参阅“警告”:
ACTIONS
-delete
Delete files; true if removal succeeded. If the removal failed,
an error message is issued. If -delete fails, find's exit status
will be nonzero (when it eventually exits). Use of -delete auto‐
matically turns on the -depth option.
Warnings: Don't forget that the find command line is evaluated as
an expression, so putting -delete first will make find try to
delete everything below the starting points you specified. When
testing a find command line that you later intend to use with
-delete, you should explicitly specify -depth in order to avoid
later surprises. Because -delete implies -depth, you cannot use‐
fully use -prune and -delete together.
[ ... ]
并且,从上面的部分开始:
OPTIONS
[ ... ]
-depth Process each directory's contents before the directory itself.
The -delete action also implies -depth.
删除文件的更快解决方案
您实际上并不需要在删除文件的同时删除目录,对吧?如果我们不删除目录,则不需要整个-depth
文件,我们只需找到一个文件并将其删除,然后按照您的建议继续执行下一个操作。
这次我们可以使用简单的打印变体来测试find
, 和 隐式-print
。
我们只想查找普通文件,没有符号链接、目录、特殊文件等:
find . -mindepth 2 -mtime +5 -type f
我们通常xargs
在每个启动的进程中删除多个文件rm
,并使用空字节作为分隔符来处理奇怪的文件名:
测试此命令 - 请注意echo
前面的rm
,因此它会打印稍后将运行的内容:
find . -mindepth 2 -mtime +5 -type f -print0 | xargs -0 echo rm
这些行会很长并且难以阅读;对于初始测试,通过添加-n 3
作为第一个参数,每行仅三个文件可以帮助获得可读输出xargs
如果一切正常,请删除echo
前面的rm
并再次运行。
那应该是快很多;
如果我们谈论的是数百万个文件 - 您编写的文件总数为 6 亿个 - 还有更多事情需要考虑:
大多数程序(包括find
)使用库调用读取目录readdir (3)
。通常使用 32 KB 的缓冲区来读取目录;当包含可能很长的文件名的巨大列表的目录很大时,这就会成为一个问题。
解决这个问题的方法是直接使用系统调用来读取目录条目,
getdents (2)
,并以更合适的方式处理缓冲。
详情请参见你可以列出一个包含800万个文件的目录!但不是用ls..
(如果您可以在问题中添加有关每个目录的典型文件数、每个目录的目录数、最大路径深度的详细信息,那将会很有趣;此外,还使用了哪个文件系统。)
(如果仍然很慢,您应该检查文件系统性能问题。)
答案2
我一直喜欢使用这个-exec
选项:
find . -mindepth 2 -mtime +5 -type f -exec rm -f {} \;
之后-v
将rm
打印将被删除的文件,但是在控制台上显示输出似乎确实会稍微减慢速度,所以如果速度是要求,我会忽略它。
答案3
使用 rsync 和该选项要快得多--delete
。只需有一个空文件夹即可同步到目标文件夹,中提琴一切都变得非常快。该rm -rf
命令很慢,因为它在删除之前检查每个索引节点的链接。