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


11

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

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

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

查找:-delete操作自动打开-depth,但是当-depth有效时,-prune不执行任何操作。如果仍要继续,则只需显式使用-depth选项。

添加-depth会导致b其中包含所有文件,这一定不会发生。

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


@MichaelKjörling:我看过extglob,但是a除了,您如何包括所有内容a/b
2013年

不行cd a && ls -d !(b/*)吗 (这样做,rm -r而不是ls -d。)
CVn

您的建议将删除子文件夹。我想保持文件夹完整。我想查找并删除树下的所有文件a(除外a/b)。
extrin

因此,只需将其跳过-r即可。通过使用bash的扩展glob,似乎可以很容易地回答您所要问的问题,然后由您决定如何处理glob的结果。
CVn

@MichaelKjörling仅仅因为两个问题的解决方案模棱两可,并不能使问题重复。解决这两个问题的大多数方法都不能解决另一个问题。
吉尔(Gilles)'所以

Answers:


13

TL; DR:最好的方法是使用-exec rm代替-delete

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

说明:

为什么找抱怨当您尝试使用-delete-prune

答案很简单:因为-delete暗示-depth-depth使-prune无效。

在得出详细答案之前,请先观察带有和不带有find的行为-depth

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

无法保证单个目录中的订单。但是可以保证目录在目录内容之前被处理。注意foo/之前foo/*foo/bar之前foo/bar/*

可以用反转-depth

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

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

更长的答案:

  • -prune防止查找下降到目录。换句话说,-prune跳过目录的内容。就您而言,-name b -prune阻止find进入名称为的任何目录b
  • -depth可以在目录本身之前处理目录的内容。这意味着到查找开始处理目录条目时,b其内容已被处理。因此-prune实际上是无效的-depth
  • -delete意味着-depth可以先删除文件,然后删除空目录。-delete拒绝删除非空目录。我猜可能会添加一个选项来强制-delete删除非空目录和/或防止-delete隐含目录-depth。不过那是另一回事了。

还有另一种方法可以实现您想要的:

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*"。这留给读者作为练习。

我假设您使用ab作为占位符,真实姓名更像allosaurusbrachiosaurus。如果您brachiosaurus替换掉,b那么误报的数量将大大减少。

至少不会误报,因此不会那么可悲。此外,您可以通过先不带命令运行命令来检查误报-delete(但请记住放置暗示的内容-depth)并检查输出。

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

-not -path就是这样!感谢您的慷慨解说!
2013年

1
为什么一些阐述-not -path,同时工作-prune并不会有帮助。为什么能-not -path与之共存-depth
Faheem Mitha 2015年

3

只需使用rm代替-delete

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

1
您能详细说明为什么rm有效delete吗?
Faheem Mitha 2015年

1
哦,我猜想可能是因为“ -delete拒绝删除非空目录。”引用@lesmana。因此拒绝删除非空目录。但是rm没有那个问题。但是,无论如何,精心设计将是一件好事。
Faheem Mitha 2015年

@FaheemMitha,问题的答案。-delete暗示-depth,这显然无法使用-prune-path可以工作,但不会停止find浏览不需要探索的目录。
斯特凡Chazelas

0

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

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

“-非路径”解决方案具有查看排除目录及以下目录中的所有文件的明显开销。

“ 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”的组合应该能够在没有-depth的情况下运行,因为它没有尝试删除目录。或者,如果“ find”具有表示不删除目录的“ -deletefile”操作,则无需暗示-depth。

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

(关闭声音模式)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.