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

for*_*rin 15 directory find rm

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

find a \( -name b -prune \) -o -type f -delete
Run Code Online (Sandbox Code Playgroud)

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

find: -delete 操作会自动打开 -depth,但 -prune 在 -depth 生效时什么也不做。如果您想继续,只需明确使用 -depth 选项。

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

任何人都知道一种安全的方法来使这项工作?

les*_*ana 17

一种方法是使用-exec rm代替-delete.

find a \( -name b -prune \) -o -type f -exec rm {} +
Run Code Online (Sandbox Code Playgroud)

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

find a -not -path "*/b*" -type f -delete
Run Code Online (Sandbox Code Playgroud)

解释为什么-prune与 碰撞-delete

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

观察使用和不使用 find 的行为-depth

$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
Run Code Online (Sandbox Code Playgroud)

不能保证单个目录中的顺序。但是可以保证目录在其内容之前被处理。foo/在 any之前foo/*foo/barany 之前注意foo/bar/*

这可以用 反转-depth

$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Run Code Online (Sandbox Code Playgroud)

请注意,现在都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
Run Code Online (Sandbox Code Playgroud)

这可能会也可能不会更容易记住。

此命令仍会下降到目录中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。如果您brachiosaurus使用 代替,b那么误报的数量将大大减少。

至少误报将不能删除,所以这将是不一样悲惨。此外,您可以通过首先运行命令而检查误报-delete(但请记住放置隐含的-depth)并检查输出。

find a -not -path "*/b*" -type f -depth
Run Code Online (Sandbox Code Playgroud)