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

7 bash shell sed pipe find-util

我有这个find命令:

find . -type f  -not -path '**/.git/**' -not -path '**/node_modules/**'  | xargs sed -i '' s/typescript-library-skeleton/xxx/g;
Run Code Online (Sandbox Code Playgroud)

由于某种原因,它给了我这些警告/错误:

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
Run Code Online (Sandbox Code Playgroud)

我甚至尝试过使用:

-not -path '**/.git/objects/**'
Run Code Online (Sandbox Code Playgroud)

得到了同样的事情.有人知道为什么查找在.git目录中搜索?看起来很奇怪.

tha*_*guy 7

为什么在.git目录中搜索?

GNU find很聪明,并支持对一个简单实现的几个优化:

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

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

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

要明确告诉它完全跳过目录,您可以改为使用-prune.请参见如何在find中排除目录.命令


Cha*_*ffy 6

更高效和更正确的方法是避免默认-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 '{}' +
Run Code Online (Sandbox Code Playgroud)

请注意以下几点:

  • 我们-prune用来告诉find甚至没有递减不需要的目录,而不是-not -path ...告诉它在找到它们之后丢弃这些目录中的名字.
  • 我们把-prune小号之前-type f,所以我们能够匹配目录修剪.
  • 我们有一个明确的操作,不依赖于默认值-print.这很重要,因为默认情况下-print有效地有一组括号:如果给出明确的操作,则find ...表现得像find '(' ... ')' -print,不喜欢find ... -print,不行.
  • 我们xargs仅使用-0支持NUL分隔输入的参数,以及侧面的-print0操作find来生成NUL分隔的名称列表.NUL是唯一不能出现在任意文件路径中的字符(是的,可以存在换行符) - 因此是唯一可以安全地用于分隔路径的字符.(如果不保证-0扩展名xargs-print0扩展名find可用,请-exec sed -i '' ... {} +改用).