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

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

我最近注意到POSIX 规范find不包括-maxdepth初级。

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

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

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

答案1

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

find . \( -name . -o -prune \) -extra-conditions-and-actions

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

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

find . ! -name . -prune -extra-conditions-and-actions

对于-maxdepth 2,则变为:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

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

例如,如果您有一个名为 的目录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

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

您可以通过执行以下操作来解决此问题:

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

但请注意,这会影响所有区域设置find及其运行的任何应用程序(例如通过-exec谓词)。

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

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

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

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

find some/dir/. ! -name . -prune ...

对于-mindepth 1 -maxdepth 1或:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

为了-maxdepth 2

我仍然会做一个:

(cd -P -- "$dir" && find . ...)

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


与否定的组合-o是运行两组独立的-condition/ -actionin的常见技巧find

如果您想-action1在文件会议上运行-condition1并独立地-action2在文件会议上运行-condition2,则不能执行以下操作:

find . -condition1 -action1 -condition2 -action2

因为-action2仅对满足以下条件的文件运行两个都状况。

也不:

find . -contition1 -action1 -o -condition2 -action2

因为-action2对于满足以下条件的文件不会运行两个都状况。

find . \( ! -condition1 -o -action1 \) -condition2 -action2

工作将\( ! -condition1 -o -action1 \)决心真的对于每个文件。假设是一个总是返回的-action1动作(如-prune, )-exec ... {} +真的。对于类似的操作-exec ... \;可能会返回错误的,您可能需要添加另一个无害但返回的-o -something位置-something真的就像-trueGNU 中的findor -links +0or ! -name ''or -name '*'(尽管请注意上面关于无效字符的问题)。

答案2

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

find . -path '*/*/*' -prune -o -type d -print

将为最大深度 1,因为*匹配.*/*matches./dir1和被修剪的*/*/*matches 。./dir1/dir2如果您使用绝对起始目录,则还需要添加/前导-path

答案3

我遇到了一个问题,我需要一种在搜索多个路径(而不仅仅是.)时限制深度的方法。

例如:

$ find dir1 dir2 -name myfile -maxdepth 1

这导致我采用了使用 -regex 的替代方法。要点是:

-regex '(<list of paths | delimited>)/<filename>'

所以,上面的内容就是:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

没有文件名:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

最后,将-maxdepth 2正则表达式更改为:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'

相关内容