Find

為什麼 GNU find -execdir 命令的行為與 BSD find 不同?

  • December 22, 2015

在我的 OSX 上,我通過以下find方式在預設 BSD 旁邊安裝了 GNU :.find``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

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 查找手冊中,我可以看到我可以指定POSIXLY_CORRECT遵循 POSIX 標準,但這不適用於上面的範例。

對於上述範例,是否有任何其他強制 GNU find 相同輸出(例如 POSIX 標準)的方法?

除了無限循環之外,為什麼 GNU 列印相對文件名而 BSD 列印完整路徑呢?

這不是無限循環,只是 GNUfind報告echo死於 SIGPIPE(因為 stdout 上管道的另一端在死亡時已關閉head)。

-execdirPOSIX 未指定。即使對於, POSIX 規範-exec中也沒有任何內容表明如果命令被 SIGPIPE 殺死,則應該退出。find

因此,POSIX 是否會指定-execdirgfind可能比您的 BSD 更符合 POSIX find(假設您的 BSD find 在其孩子死於 SIGPIPE 時退出,正如您的問題的措辭所暗示的那樣,FreeBSDfind不在我的測試中並且確實echo 在循環中執行每個文件(就像 GNU find 一樣,不是無窮無盡的))。

您可能會說,對於最常見的情況,find最好在孩子死於 SIGPIPE 時退出,但由於-execstdout 上的管道被關閉之外的其他原因,ute 命令仍然可能死於 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對其進行了一些有意的更改(改進)。例如,它./在傳遞給命令的參數中添加文件名。這意味著以例如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 {} \;

或者更有效:

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。具有該腳本的腳本將不符合要求。

POSIXfind要求至少給出一個路徑,但如果第一個非選項參數find-or 開頭是find謂詞(如!,或(),則行為未指定,因此 GNUfind行為兼容的,報告錯誤(或將第一個參數視為文件路徑,即使它表示查找謂詞)或在您的臉上噴紅漆,沒有任何理由POSIXLY_CORRECT會影響find那裡的行為。

引用自:https://unix.stackexchange.com/questions/250191