“查找”命令可以更有效地删除许多文件吗?

don*_*llo 7 find

我想删除在多个子目录中有大量文件的目录中的旧文件。

我正在尝试使用以下方法 - 经过一些谷歌搜索后,它似乎是推荐且有效的方法:

find . -mindepth 2 -mtime +5 -print -delete
Run Code Online (Sandbox Code Playgroud)

我的期望是,这应该打印一个满足条件的文件(5 天前修改并满足 mindepth 条件),然后将其删除,然后继续下一个文件。

但是,当这个命令运行时,我可以看到 find 的内存使用量在增加,但没有打印任何内容(因此我认为还没有删除任何内容)。这似乎意味着find首先收集满足条件的所有文件,在遍历整个文件系统树后,它会打印然后删除文件。

有没有办法让它在对文件运行测试后立即删除它?这将有助于逐步清理 - 我可以选择终止命令,然后稍后重新运行它(这将有效地恢复文件删除)。这目前似乎没有发生,因为 find 在遍历巨大的文件系统树之前还没有开始删除任何内容。有没有办法解决?

编辑 - 包括有关我的用例的请求数据:

我必须清理的目录最大深度约为 4;常规文件仅存在于文件系统的叶子上。大约有大约 6 亿个常规文件,其中叶目录最多包含 5 个文件。较低级别的目录扇出大约为 3。较高级别的扇出很大。单个 7.2TB LVM 磁盘(带有 4 个约 2 TB 的物理硬盘)上占用的总空间为 6.5TB

Vol*_*gel 13

find命令慢的原因

这是一个非常有趣的问题......或者,老实说,恶意

命令

find . -mindepth 2 -mtime +5 -print -delete

与通常的试用版本非常不同,省略了危险的部分,-delete

find . -mindepth 2 -mtime +5 -print

棘手的部分是 action-delete 隐含了 option -depth。包括删除的命令真的是

find . -depth -mindepth 2 -mtime +5 -print -delete

并且应该用

find . -depth -mindepth 2 -mtime +5 -print

这与你看到的症状密切相关;该选项-depth是将文件系统树的树遍历算法从preorder depth-first search更改为inorder depth-first search
以前,到达的每个文件或目录都会立即使用,然后被遗忘。Find 正在使用树本身来找到它的方式。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.
    [ ... ]
Run Code Online (Sandbox Code Playgroud)

而且,从更进一步的部分:

 OPTIONS
    [ ... ]
    -depth Process each directory's contents  before  the  directory  itself.
           The -delete action also implies -depth.
Run Code Online (Sandbox Code Playgroud)


删除文件的更快解决方案

您真的不需要在删除文件的同一运行中删除目录,对吗?如果我们不删除目录,我们不需要整个-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..


(如果您可以在您的问题中添加有关每个目录的典型文件数、每个目录的目录、路径的最大深度;另外,使用哪个文件系统的详细信息,那将会很有趣。)

(如果它仍然很慢,您应该检查文件系统性能问题。)