Bash - 使用 find 命令排除子目录

Bash - Excluding subdirectories using the find command

我正在使用查找命令获取特定文件所在的文件夹列表。但是由于某些子目录的权限被拒绝错误,我想排除某个子目录名称。 我已经尝试过我在这里找到的这些解决方案:

find /path/to/folders -path "*/noDuplicates" -prune -type f -name "fileName.txt"

find /path/to/folders ! -path "*/noDuplicates" -type f -name "fileName.txt"

以及这些命令的一些变体(例如路径名的变体)。 在第一种情况下它根本找不到文件夹,在第二种情况下我再次收到错误,所以我猜它仍然会尝试访问该目录。有谁知道我做错了什么或者有人对此有不同的解决方案?

问题在于 find 计算您传递给 -path 选项的表达式的方式。 相反,您应该尝试类似的方法:

find /path/to/folders ! -path "*noDuplicates*" -type f -name "fileName.txt"

根据我之前的评论,这适用于我的 Debian:

find . -path ./ignored_directory -prune -o -name fileName.txt -print

find /path/to/folder -path "*/ignored_directory" -prune -o -name fileName.txt -print

find /path/to/folder -name fileName.txt -not -path "*/ignored_directory/*"

对差异进行了很好的辩论here

编辑(添加了行为规范详细信息)

正在删除查找中的所有权限被拒绝的目录

使用 gnufi​​nd。

规范行为细节 - 在这个解决方案中我们想要:

  1. 排除不可读的目录内容(修剪它们),
  2. 避免 "permission denied" 来自不可读目录的错误,
  3. 保留其他错误和 return 状态,但
  4. 处理所有文件(即使是不可读的文件,如果我们能读出它们的名字)

基本设计模式是:

find ...  \( -readable -o -prune \) ...

例子

find /var/log/ \( -readable -o -prune \) -name "*.1" 

\谢谢{mklement0}

为了补充 并解决 OP 在 -o 的需求的困惑:

  • -prune,作为每个 find 主要(动作或测试,在 GNU 语言中),returns 一个 布尔值,以及那个布尔值在 -prune.
  • 的情况下 总是 true
  • 没有显式运算符,初选与 -a (-and) 隐式连接,它像它的兄弟 -o (-or) 一样执行 短路 布尔逻辑。
  • -a 的优先级高于 -o.

有关所有 find 概念的摘要,请参阅

因此,接受的答案

find . -path ./ignored_directory -prune -o -name fileName.txt -print

等价于(括号用于明确计算优先级):

find . \( -path ./ignored_directory -a -prune \) \
       -o \
       \( -name fileName.txt -a -print \)

由于短路应用,计算如下:

  • 匹配 ./ignored_directory 的输入路径导致 -prune 被评估;因为 -prune 总是 returns true,短路会阻止计算 -o 运算符的 right 侧;实际上,没有任何反应(输入路径被忽略)
  • 输入路径不匹配 ./ignored_directory,立即 - 再次由于短路 - 在 -o 右侧 侧继续评估:
    • 只有当输入路径的文件名部分与 fileName.txt 匹配时,才会对 -print 进行主要评估;实际上,只打印文件名匹配 fileName.txt 的输入路径。

编辑:尽管我最初在这里声称,-print 需要在 -o 的右侧这里;没有它,implied -print 将应用于 entire 表达式,因此 also 打印左手手边匹配;请参阅下面的背景信息。


相比之下,让我们考虑一下 错误地不使用 -o 会造成什么影响:

find . -path ./ignored_directory -prune -name fileName.txt -print

这相当于:

find . -path ./ignored_directory -a -prune -a -name fileName.txt -a -print

这将打印修剪路径(也匹配-name过滤器),因为-name-print 初选(隐含地)与逻辑与连接;
在这种特定情况下,由于 ./ignored_directory 也不能匹配 fileName.txt,因此 什么都不会打印 ,但是如果 -path 的参数是 glob,它可能得到输出。


关于 find 隐式 使用 -print:

POSIX mandates 如果 find 命令的表达式作为一个整体不包含任何一个

  • 产出初选,例如-print本身
  • 执行某事的初选,例如-exec-ok
  • (给出的示例初选对于 POSIX 规范来说是详尽无遗的。find,但现实世界的实现如 GNU find 和 BSD find 添加其他的,例如产生输出的 -print0 主,以及执行 -execdir 主)

-print 应用隐式,就好像表达式已被指定为:

\( expression \) -print

这很方便,因为它允许您编写 find . 之类的命令,而无需附加 -print.

但是,在某些情况下,需要明确的-print,例如这里的情况:

假设我们没有在接受的答案末尾指定 -print

find . -path ./ignored_directory -prune -o -name fileName.txt

由于表达式中现在没有输出生成或执行主体,因此它的计算结果为:

find . \( -path ./ignored_directory -prune -o -name fileName.txt \) -print

这不会按预期工作,因为如果 entire 括号表达式的计算结果为真,它将打印路径,在这种情况下错误地包括修剪目录。

相比之下,通过显式将 -print 附加到 -o 分支,仅当 -o 表达式的右侧计算结果为真时才打印路径;使用括号使逻辑更清晰:

find . -path ./ignored_directory -prune -o \( -name fileName.txt -print \)

相反,如果左侧为真,则仅执行 -prune,不会产生任何输出(并且由于 overall 表达式包含 -print-print 未隐式应用)。