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

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

在我的 OSX 上,我通过以下方式find在默认 BSD 旁边安装了 GNU : 。findbrew 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

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

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

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

对于上面的示例,还有其他方法可以强制 GNU find 具有相同的输出(例如 POSIX 标准)吗?

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

答案1

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

-execdirPOSIX 没有指定。即使对于-exec,也没有任何东西POSIX 规范这表示如果该命令被 SIGPIPE 终止,find则应该退出。

因此,如果 POSIX 指定-execdirgfind可能会比您的 BSD 更符合 POSIX find(假设您的 BSD 在其子进程死于 SIGPIPE 时发现退出,正如您的问题的措辞所暗示的那样,FreeBSDfind不在我的测试中并且确实运行echo 循环中对于每个文件(就像 GNU find,不是无限的))。

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

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

find . ... \( -exec echo {} \; -o -quit \)

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

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

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

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

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

$ find /dev/null -exec blah \;; echo "$?"
find: `blah': No such file or directory
0

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

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

(trap '' PIPE; find -L /etc -execdir echo {} \; | head)

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


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

再说一次,-execdir作为一个标准选项,没有文字表明谁对谁错。-execdir是由 BSD 引入的find,后来被 GNU 复制find

GNUfind对其做了一些有意的改变(改进)。例如,它./在传递给命令的参数中添加文件名。这意味着以examplefind . -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 {} \;

或者更有效:

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

你可以简化为

find -L /etc | awk -F / '{print $NF}'

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

GNUly:

find -L /etc -printf '%f\n'

关于是否:

find -exec echo {} \;

在里面关联你指的是 POSIX 与否。

不,作为命令调用,这不是 POSIX。 A脚本那将是不合规的。

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

相关内容