為什麼 GNU find -execdir 命令的行為與 BSD find 不同?
在我的 OSX 上,我通過以下
find
方式在預設 BSD 旁邊安裝了 GNU :.find``brew install findutils
據我了解,BSD
find
遵循 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 列印完整路徑呢?
這不是無限循環,只是 GNU
find
報告echo
死於 SIGPIPE(因為 stdout 上管道的另一端在死亡時已關閉head
)。
-execdir
POSIX 未指定。即使對於, POSIX 規範-exec
中也沒有任何內容表明如果命令被 SIGPIPE 殺死,則應該退出。find
因此,POSIX 是否會指定
-execdir
,gfind
可能比您的 BSD 更符合 POSIXfind
(假設您的 BSD find 在其孩子死於 SIGPIPE 時退出,正如您的問題的措辭所暗示的那樣,FreeBSDfind
不在我的測試中並且確實echo
在循環中執行每個文件(就像 GNU find 一樣,不是無窮無盡的))。您可能會說,對於最常見的情況,
find
最好在孩子死於 SIGPIPE 時退出,但由於-exec
stdout 上的管道被關閉之外的其他原因,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 和 FreeBSD
find
在以下情況下都是不兼容的:$ 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
報告錯誤消息。現在關於
filename
vs/etc/filename
vs./filename
正在顯示。同樣,
-execdir
作為不是標準選項,沒有文字說明誰對誰錯。-execdir
由 BSD 引入,find
後來被 GNU 複製find
。GNU
find
對其進行了一些有意的更改(改進)。例如,它./
在傳遞給命令的參數中添加文件名。這意味著以例如find . -execdir cmd {} \;
開頭的文件名沒有問題。-
-L -execdir
不傳遞相對於父目錄的文件路徑這一事實實際上是一個影響 GNU 版本 4.3.0 到 4.5.8 的錯誤find
。它已在 4.5.9 中修復,但那是在開發分支上,自那以後(截至 2015 年 12 月 22 日,儘管即將發布)還沒有新的穩定版本。如果你想要的只是列印每個文件的基本名稱
/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。具有該腳本的腳本將不符合要求。
POSIX
find
要求至少給出一個路徑,但如果第一個非選項參數find
以-
or 開頭是find
謂詞(如!
,或(
),則行為未指定,因此 GNUfind
行為是兼容的,報告錯誤(或將第一個參數視為文件路徑,即使它表示查找謂詞)或在您的臉上噴紅漆,沒有任何理由POSIXLY_CORRECT
會影響find
那裡的行為。