将 POSIX 查找限制为特定深度?

Wil*_*ard 23 find posix

我最近注意到POSIX 规范find不包括-maxdepth主要的。

对于不熟悉的人来说,-maxdepth初级的目的是限制find下降的深度。 -maxdepth 0结果只处理命令行参数;-maxdepth 1只会直接在命令行参数等中处理结果。

如何-maxdepth仅使用 POSIX 指定的选项和工具获得与非 POSIX主节点等效的行为?

(注意:当然我可以-maxdepth 0通过仅用-prune作第一个操作数来获得等价物,但这不会扩展到其他深度。)

Sté*_*las 21

@meuh 的方法效率低下,因为他的-maxdepth 1方法仍然允许find读取 1 级目录的内容,否则以后忽略它们。如果某些目录名称包含在用户语言环境中不构成有效字符的字节序列(例如不同字符编码的文件名),它也将无法与某些find实现(包括 GNU find)一起正常工作。

find . \( -name . -o -prune \) -extra-conditions-and-actions
Run Code Online (Sandbox Code Playgroud)

是实现 GNU 的更规范的方式-maxdepth 1

一般来说,你想要的深度 1 ( -mindepth 1 -maxdepth 1) 因为你不想考虑.(depth 0),然后它甚至更简单:

find . ! -name . -prune -extra-conditions-and-actions
Run Code Online (Sandbox Code Playgroud)

对于-maxdepth 2,它变成:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions
Run Code Online (Sandbox Code Playgroud)

这就是您遇到无效字符问题的地方。

例如,如果您有一个名为Stéphaneé以 iso8859-1(又名 latin1)字符集(0xe9 字节)编码的目录,这在 2000 年代中期之前在西欧和美洲最常见,那么该 0xe9 字节不是UTF-8 中的有效字符。所以,在UTF-8语言中,*通配符(与某些find实现方式)将不匹配Stéphane*是0或更大的字符和0xe9不是字符。

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
Run Code Online (Sandbox Code Playgroud)

我的find(当输出到终端时)显示无效的 0xe9 字节,?如上所述。你可以看到这St<0xe9>phane/Chazelas不是pruned。

您可以通过执行以下操作来解决它:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions
Run Code Online (Sandbox Code Playgroud)

但请注意,这会影响find它运行的任何应用程序的所有语言环境设置(例如通过-exec谓词)。

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith
Run Code Online (Sandbox Code Playgroud)

现在,我真的明白了,-maxdepth 2但请注意,在 UTF-8 中正确编码的第二个 Stéphane 中的 é 如何显示为 é 的 UTF-8 编码??的 0xc3 0xa9 字节(在 C 语言环境中被视为两个单独的未定义字符)是在 C 语言环境中不可打印的字符。

如果我添加了一个-name '????????',我就会得到错误的 Stéphane(编码为 iso8859-1 的那个)。

要应用于任意路径而不是.,您可以执行以下操作:

find some/dir/. ! -name . -prune ...
Run Code Online (Sandbox Code Playgroud)

对于-mindepth 1 -maxdepth 1或:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...
Run Code Online (Sandbox Code Playgroud)

-maxdepth 2

我仍然会做一个:

(cd -P -- "$dir" && find . ...)
Run Code Online (Sandbox Code Playgroud)

首先是因为这使得路径更短,这使得它不太可能遇到路径太长arg 列表太长的问题,但也解决了find不能支持任意路径参数(除了-fFreeBSD find)的事实,因为它会窒息$dirlike!-print... 的值


-o与否定组合是一个常见的特技运行两个独立的组-condition/-actionfind

如果要-action1在 files meeting 上-condition1独立-action2运行 files meeting -condition2,则不能执行以下操作:

find . -condition1 -action1 -condition2 -action2
Run Code Online (Sandbox Code Playgroud)

As-action2只会对同时满足这两个条件的文件运行。

也不:

find . -contition1 -action1 -o -condition2 -action2
Run Code Online (Sandbox Code Playgroud)

对于-action2满足这两个条件的文件,不会运行As 。

find . \( ! -condition1 -o -action1 \) -condition2 -action2
Run Code Online (Sandbox Code Playgroud)

就像对每个文件\( ! -condition1 -o -action1 \)解析为true 一样工作。假设-action1是一个总是返回true的操作(如-prune, -exec ... {} +)。对于这样的行为可能返回错误,你可能要添加另一个地方是无害的,但返回像在GNU或或或(但要注意上面关于无效字符的问题)。-exec ... \;-o -something-something-truefind-links +0! -name ''-name '*'

  • 总有一天我会遇到一堆中文文件,我会很高兴我已经阅读了您关于语言环境和有效字符的许多答案。:) (2认同)
  • @Wildcard,你(更是如此中国人)更有可能遇到英国、法国的问题......文件名而不是中文文件名,因为中文文件名比字母脚本的文件名更常以UTF-8编码这通常可以由单字节字符集覆盖,直到最近才成为规范。还有其他多字节字符集可以覆盖汉字,但我预计中国人会比西方人更早地切换到 UTF-8,因为这些字符集有许多令人讨厌的问题。另请参阅编辑示例。 (2认同)

meu*_*euh 8

您可以使用-path来匹配给定的深度并在那里进行修剪。例如

find . -path '*/*/*' -prune -o -type d -print
Run Code Online (Sandbox Code Playgroud)

将是 maxdepth 1,因为*匹配.*/*matches./dir1和被修剪的*/*/*匹配./dir1/dir2。如果您使用的是绝对起始目录,你需要一个领导添加/-path了。