使用find删除除某个子目录之外的所有文件

使用find删除除某个子目录之外的所有文件

我想递归删除文件夹中一段时间​​内未访问的所有文件a,子文件夹中的所有文件除外b

find a \( -name b -prune \) -o -type f -delete

但是,我收到一条错误消息:

find:-delete 操作会自动打开 -deep,但 -prune 在 -deep 生效时不执行任何操作。如果您想继续,只需显式使用 -depth 选项。

添加会导致包含-depth所有文件,这必须b不是发生。

有人知道一种安全的方法来完成这项工作吗?

答案1

一种方法是使用-exec rm而不是-delete.

find a \( -name b -prune \) -o -type f -exec rm {} +

或者使用-not -path代替-prune

find a -not -path "*/b*" -type f -delete

解释为什么-prune与 发生冲突-delete

当您尝试使用-deletewith时,会发现抱怨,-prune因为-delete暗示-depth-depth使得-prune无效。

观察带有和不带有 的 find 的行为-depth

$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2

无法保证单个目录中的顺序。但可以保证目录先于其内容被处理。请注意foo/any 之前foo/*foo/barany 之前foo/bar/*

这可以用 逆转-depth

$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/

请注意,现在都foo/*出现在 之前foo/。与 相同foo/bar

更多解释:

  • -prune防止 find 下降到目录中。换句话说,-prune跳过目录的内容。在您的情况下-name b -prune,这意味着当 find 到达具有该名称的目录时,b它将跳过该目录,包括所有子目录。
  • -depth使 find 在目录本身之前处理目录的内容。这意味着当 find 开始处理目录条目时,b其内容已经被处理。因此-prune无效与-depth有效。
  • -delete意味着-depth它可以先删除内容,然后删除空目录。-delete拒绝删除非空目录。

替代方法说明:

find a -not -path "*/b*" -type f -delete

这可能更容易记住,也可能不太容易记住。

该命令仍然下降到目录b并处理其中的每个文件,只是为了-not拒绝它们。如果目录b很大,这可能会导致性能问题。

-path工作方式与 不同-name-name仅与(文件或目录的)名称匹配,而-path与整个路径匹配。例如观察路径/home/lesmana/foo/bar-name bar将匹配,因为名称是bar.-path "*/foo*"将匹配,因为该字符串/foo位于路径中。-path有一些复杂之处,您在使用之前应该了解它。请阅读 的手册页find以获取更多详细信息。

请注意,这并不是 100% 万无一失。存在“误报”的可能性。上面的命令编写方式将跳过任何具有名称以b(正)开头的父目录的文件。但它也会跳过名称开头的任何文件,b无论在树中的位置如何(误报)。这可以通过编写比 更好的表达式来解决"*/b*"。这留给读者作为练习。

我假设您使用aandb作为占位符,而真实姓名更像是allosaurusand brachiosaurus。如果你将其取代brachiosaurusb那么误报的数量将大大减少。

至少误报是不是删除了,这样就不会那么悲惨了。此外,您可以通过首先运行不带命令的命令-delete(但记住放置隐含的-depth)并检查输出来检查误报。

find a -not -path "*/b*" -type f -depth

答案2

只需使用rm而不是-delete

find a -name b -prune -o -type f -exec rm -f {} +

答案3

上面的回答和解释非常有帮助。

我使用“-exec rm {} +”或“-not -path ... -delete”的解决方法,但这些可能比“find ... -delete”慢得多。我见过“find ...在 NFS 文件系统上的深层目录上,“-delete”的运行速度比“-exec rm {} +”快 5 倍。

“-not path”解决方案的明显开销是查看排除目录及以下目录中的所有文件。

“find .. -exec rm {} +”调用 rm 进行系统调用:

fstatat(AT_FDCWD, path...); 
unlinkat(AT_FDCWD, path, 0)

“find -delete”执行系统调用:

 fd=open(dir,...);
 fchdir(fd); 
 fstatat(AT_FDCWD, filename,...)
 unlinkat(dirfd, filename,...)

因此,“-exec rm {}+” rm 命令对每个文件执行 inode 完整路径查找两次,但“find -delete”对当前目录中的文件名进行统计和取消链接。当您删除一个目录中的大量文件时,这是一个巨大的胜利。

(哀鸣模式开启(抱歉))

似乎 -depth、-delete 和 -prune 之间的交互设计不必要地消除了执行常见操作“删除除 -prune 目录中的文件之外的文件”的最有效方法

“-type f -delete”的组合应该能够在没有 -deep 的情况下运行,因为它不尝试删除目录。或者,如果“find”有一个“-deletefile”操作,表示不删除目录,则不需要暗示-深度。

如果 rm 可以选择对文件名进行排序、打开目录并执行 unlinkat(dir_fd,filename) 而不是取消链接完整路径,则 xargs 或 find -exec 对 rm 命令的调用可以加快。当使用 -r 选项递归目录时,它已经执行了 unlinkat(dir_fd,filename) 。

(呜呜声模式关闭)

相关内容