为什么我的 `find` 命令给出与忽略目录相关的错误?

Why is my `find` command giving me errors relating to ignored directories?

我有这个查找命令:

find . -type f  -not -path '**/.git/**' -not -path '**/node_modules/**'  | xargs sed -i '' s/typescript-library-skeleton/xxx/g;

出于某种原因,它给了我这些 warnings/errors:

find: ./.git/objects/3c: No such file or directory
find: ./.git/objects/3f: No such file or directory
find: ./.git/objects/41: No such file or directory

我什至尝试使用:

-not -path '**/.git/objects/**'

得到了同样的东西。有人知道为什么 find 在 .git 目录中搜索吗?好像很奇怪。

更有效和更正确的方法是避免默认的 -print 操作,将 -not -path ... 更改为 -prune,并确保 xargs 仅与 NUL 一起使用-分隔输入:

find . -name .git -prune -o \
       -name node_modules -prune -o \
       -type f -print0 | xargs -0 sed -i '' s/typescript-library-skeleton/xxx/g '{}' +

注意以下几点:

  • 我们使用 -prune 告诉 find 甚至不要递归不需要的目录,而不是 -not -path ... 告诉它在 之后丢弃那些目录中的名称他们被发现.
  • 我们将 -prunes 放在 -type f 之前,这样我们就可以匹配目录进行修剪。
  • 我们有一个 显式操作 ,不依赖于默认值 -print。这很重要,因为默认的 -print 实际上有一组括号:find ... 表现得像 find '(' ... ')' -print,而不像 find ... -print,如果给出显式操作则不会。
  • 我们仅将 xargs 与启用 NUL 分隔输入的 -0 参数一起使用,并在 find 端使用 -print0 操作来生成 NUL 分隔列表的名字。 NUL 是唯一不能出现在任意文件路径中的字符(是的,换行符可以出现)——因此 only 字符可以安全地用于分隔路径。 (如果 xargs-0 扩展和 find-print0 扩展不能保证可用,请改用 -exec sed -i '' ... {} +

why is the find searching in the .git directory?

GNU find 很聪明并且支持 several optimizations 而不是简单的实现:

  • 它可以翻转 -size +512b -name '*.txt' 的顺序并首先检查名称,因为查询大小将需要第二次系统调用。
  • 它可以计算一个目录的硬链接来确定子目录的数量,当它看到所有的时候就不再需要检查它们以进行 -type d 或递归。
  • 它甚至可以重写(-B -or -C) -and -A,这样如果检查同样昂贵且没有副作用,那么-A将首先被评估,希望在1次而不是2次测试后拒绝文件.

但是,它还不够聪明,无法意识到-not -path '*/.git/*'意味着如果你找到一个目录.git那么你甚至不需要递归到它,因为里面的所有文件都将无法匹配。

相反,它尽职尽责地递归,找到每个文件并将其与模式匹配,就好像它是一个黑盒子一样。

要明确告诉它完全跳过一个目录,您可以改用 -prune。参见 How to exclude a directory in find . command