我最近注意到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
不是prune
d。
您可以通过执行以下操作来解决此问题:
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
不能支持任意路径参数的事实(除了-f
FreeBSD ),因为它会因like或...find
的值而窒息$dir
!
-print
与否定的组合-o
是运行两组独立的-condition
/ -action
in的常见技巧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
真的就像-true
GNU 中的find
or -links +0
or ! -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}[^/]*$'