为什么 GNU find -execdir 命令的行为与 BSD find 不同?

ken*_*orb 4 osx find gnu posix

在我的OSX,我已经安装了GNUfind旁边默认BSDfind通过:brew install findutils

据我了解,BSDfind遵循 POSIX 标准,而 GNU 将其设为可选(根据这篇文章),这在预期输出中造成了很多不一致。

例如:

BSD 查找

$ find -L /etc -execdir echo {} ';' | head
etc
AFP.conf
afpovertcp.cfg
aliases
aliases.db
apache2
extra
httpd-autoindex.conf
httpd-dav.conf
httpd-default.conf
Run Code Online (Sandbox Code Playgroud)

GNU 查找

$ gfind --version
find (GNU findutils) 4.4.2
$ POSIXLY_CORRECT=1 gfind -L /etc -execdir echo {} ';' | head
/etc
/etc/AFP.conf
/etc/afpovertcp.cfg
/etc/aliases
/etc/aliases.db
/etc/apache2
/etc/apache2/extra
/etc/apache2/extra/httpd-autoindex.conf
/etc/apache2/extra/httpd-dav.conf
/etc/apache2/extra/httpd-default.conf
gfind: `echo' terminated by signal 13
gfind: `echo' terminated by signal 13
... endless loop here
Run Code Online (Sandbox Code Playgroud)

注意:我使用-L上面作为我/etc的链接到private/etc.

在 GNU 查找手册中,我可以看到我可以指定POSIXLY_CORRECT遵循 POSIX 标准,但这不适用于上面的示例。

对于上述示例,是否有其他方法可以为 GNU find 强制相同的输出(例如 POSIX 标准)?

除了无限循环,为什么 GNU 打印相对文件名而 BSD 打印完整路径?

Sté*_*las 10

这不是无限循环,只是 GNUfind报告echo因 SIGPIPE而死亡(因为 stdout 上管道的另一端在head死亡时已关闭)。

-execdirPOSIX 未指定。即使对于-execPOSIX 规范中也没有说如果命令被 SIGPIPE 杀死,find应该退出。

因此,将POSIX规定-execdirgfind可能会更POSIX符合的比你的BSD find(在其子死于SIGPIPE你的问题的措辞假设你的BSD发现退出建议,FreeBSD的find并不在我的测试,并没有运行echo 在一个循环的每个文件(如 GNU 查找,不是无穷无尽的))。

您可能会说,对于大多数常见情况,最好find在孩子死于 SIGPIPE 时退出,但由于-exec标准输出上的管道关闭之外的其他原因,ted 命令仍然可能死于 SIGPIPE,因此退出find是可接受的。

使用 GNU findfind如果命令失败,您可以告诉退出:

find . ... \( -exec echo {} \; -o -quit \)
Run Code Online (Sandbox Code Playgroud)

至于是否find允许或禁止实现报告孩子死于 stderr 上的信号,在这里(使用-execdir)我们无论如何都超出了 POSIX 的范围,但如果-exec使用 代替-execdir,似乎这将是一个gfind 不符合的情况。

规范find说:“标准错误应仅用于诊断消息”,但也

默认行为:当此部分被列为“标准错误应仅用于诊断消息。”时,这意味着除非另有说明,否则仅当退出状态指示错误时才将诊断消息发送到标准错误发生,并且该实用程序的使用方式如本卷 POSIX.1-2008 所述。

这表明由于find在这种情况下不会以非零退出状态返回,因此不应在 stderr 上输出该消息。

请注意,根据该文本,find在以下情况下GNU 和 FreeBSD 都不合规:

$ find /dev/null -exec blah \;; echo "$?"
find: `blah': No such file or directory
0
Run Code Online (Sandbox Code Playgroud)

其中两者都报告错误而不将退出状态设置为非零。这就是为什么我在 austin-group(POSIX 背后的人)邮件列表上提出这个问题

请注意,如果您将命令更改为:

(trap '' PIPE; find -L /etc -execdir echo {} \; | head)
Run Code Online (Sandbox Code Playgroud)

echo仍然会为每个文件运行,仍然会失败,但这一次,它将echo报告错误消息。


现在关于filenamevs /etc/filenamevs./filename正在显示。

同样,-execdir作为标准选项,没有文字说明谁对谁错。-execdir由 BSD 引入,find后来被 GNU 复制find

GNUfind已经对其进行了一些有意的更改(改进)。例如,它./在传递给命令的参数中添加文件名。这意味着例如find . -execdir cmd {} \;以开头的文件名没有问题-

-L -execdir不传递相对于父目录的文件路径的事实实际上是一个影响 GNU 4.3.0 到 4.5.8 版本的错误find。它在 4.5.9 中得到修复,但那是在开发分支上,从那以后就没有新的稳定版本(截至 2015 年 12 月 22 日,尽管即将发布)。

findutils 邮件列表中的更多信息

如果您想要的/etc只是可移植地打印每个文件的基本名称,您可以这样做:

find -L /etc -exec basename {} \;
Run Code Online (Sandbox Code Playgroud)

或更有效地:

find -L ///etc | awk -F / '/\/\// && NR>1 {print last}
                          {if (NF > 1) last = $NF
                           else last = last "\n" $NF}
                          END {if (NR) print last}'
Run Code Online (Sandbox Code Playgroud)

您可以简化为

find -L /etc | awk -F / '{print $NF}'
Run Code Online (Sandbox Code Playgroud)

如果你能保证文件路径不包含换行符(IIRC,尽管某些版本的 OS/X 在 /etc 中有这样的文件)。

GNUly:

find -L /etc -printf '%f\n'
Run Code Online (Sandbox Code Playgroud)

关于是否:

find -exec echo {} \;
Run Code Online (Sandbox Code Playgroud)

在您所指的链接中,是否为 POSIX。

不,作为命令调用,这不是 POSIX。一个脚本,将有这将是不符合规定的。

POSIXfind要求至少给出一个路径,但如果第一个非选项参数find-或开头是find谓词(如!, or (),则不指定find行为,因此 GNU行为兼容的,报告错误的实现也是如此(或将第一个参数视为文件路径,即使它代表一个 find 谓词)或在你脸上喷红漆,没有理由POSIXLY_CORRECT会影响find那里的行为。