为什么我的 `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 ...
告诉它在 之后丢弃那些目录中的名称他们被发现.
- 我们将
-prune
s 放在 和 -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
我有这个查找命令:
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 ...
告诉它在 之后丢弃那些目录中的名称他们被发现. - 我们将
-prune
s 放在 和-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