我想递归删除文件夹中一段时间内未访问的所有文件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
:
当您尝试使用-delete
with时,会发现抱怨,-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/bar
any 之前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*"
。这留给读者作为练习。
我假设您使用a
andb
作为占位符,而真实姓名更像是allosaurus
and brachiosaurus
。如果你将其取代brachiosaurus
,b
那么误报的数量将大大减少。
至少误报是不是删除了,这样就不会那么悲惨了。此外,您可以通过首先运行不带命令的命令-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) 。
(呜呜声模式关闭)