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


15

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

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

-maxdepth仅使用POSIX指定的选项和工具,如何获得与非POSIX 主要对象相同的行为?

(注意:当然-maxdepth 0,只要将其-prune用作第一个操作数,就可以得到与之相当的结果,但这不会扩展到其他深度。)


@StevenPenny,FreeBSD的-depth -2-depth 1...做法可以被看作是比GNU的好-maxdepth/-mindepth
斯特凡Chazelas

@StéphaneChazelas任一种方式-POSIX查找应具有一个或另一个;否则它会残缺
史蒂文·彭妮

1
至少对于-maxdepth/ -mindepth,有一些合理的选择(请注意,这-path是POSIX的最新功能)。-timexy-mtime -3m(或-mmin -3)的替代方案繁琐得多。一些喜欢-execdir/ -delete没有可靠的选择。
斯特凡Chazelas

2
@StevenPenny,请随时在austingroupbugs.net上记录票证以请求添加票证。我已经看到,在有充分理由的情况下,无需赞助商即可添加更多内容。可能更好的做法是首先添加尽可能多的实现,因此POSIX只需要指定通常不那么有争议的现有方法即可。
斯特凡Chazelas

在我的情况下,@StéphaneChazelas我最终只是直接命名文件,但是谢谢你。如果情况再次出现,我可能会提交票证
史蒂文·彭妮

Answers:


7

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

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

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


嗯,棘手。您不能只/*从模式的末尾删除一层,取出-o运算符,然后得到相同的结果吗?
通配符

不,因为也*匹配/,所以可悲的是,dir a/b/c/d/e适合-path */*
meuh's

但是a/b/c/d/e永远也不会达到,因为-prune它将应用于a/b....
Wildcard

1
抱歉,我读错了-prune,将-o其删除。如果保留,-prune则问题在于*/*不会匹配maxdepth之上的任何内容,例如single directory a
meuh's

11

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

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

是实现GNU -maxdepth 1(或FreeBSD -depth -2)的更规范的方法。

虽然一般来说,是-depth 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但是é在2000年代中期之前在西欧和美洲最常见,但使用iso8859-1(aka latin1)字符集(0xe9字节)进行编码,则该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编码的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 除外find),因为它会阻塞$dir喜欢!-print...的值


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

如果要-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-links +0-name '*'(但要注意上面关于无效字符的问题)。


1
总有一天,我会遇到很多中文文件,我很高兴阅读您关于语言环境和有效字符的许多答案。:)
通配符

2
@Wildcard,您(甚至更是一个中国人)比起中文文件名,您更容易遇到英式,法文...文件名,因为中文文件名比字母脚本的文件名更经常用UTF-8编码通常可以用单字节字符集覆盖,直到最近才开始使用。还有其他多字节字符集可以覆盖汉字,但是我希望中国人比西方人更早使用UTF-8,因为这些字符集存在许多令人讨厌的问题。另请参见编辑示例。
斯特凡Chazelas

0

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

例如:

$ 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}[^/]*$'


1
这个问题要求一个标准的(如POSIX)解决方案。也-maxdepth可以用于多个搜索路径。
库萨兰达
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.