我最近注意到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
不是prune
d。
您可以通过执行以下操作来解决它:
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
不能支持任意路径参数(除了-f
FreeBSD find
)的事实,因为它会窒息$dir
like!
或-print
... 的值
在-o
与否定组合是一个常见的特技运行两个独立的组-condition
/-action
中find
。
如果要-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
-true
find
-links +0
! -name ''
-name '*'
您可以使用-path
来匹配给定的深度并在那里进行修剪。例如
find . -path '*/*/*' -prune -o -type d -print
Run Code Online (Sandbox Code Playgroud)
将是 maxdepth 1,因为*
匹配.
、*/*
matches./dir1
和被修剪的*/*/*
匹配./dir1/dir2
。如果您使用的是绝对起始目录,你需要一个领导添加/
到-path
了。