在我用來收集當前目錄中的一些文件的腳本中find
,如
$ find . -name "*.h"
./foo.h
現在我希望它只輸出foo.h
,沒有./
前綴。我認為空字串""
表示 shell 命令中的當前目錄。但這給了:
$ find "" -name "*.h"
find: ftsopen: No such file or directory
所以我錯了。現在我的問題是何時/如何/在哪裡/..“空字串(?)”是否表示需要檔案名稱或路徑名稱的命令中的當前目錄?有簡潔且有啟發性的解釋嗎?
一個附帶問題是上面的 find 挑剔是否可以簡單地解決,而不需要字串操作 la ${parameter#word}
or cut
or sed
?
答案1
答案2
您可以使用:
find * -name "*.h"
請注意,當前目錄中名稱以 開頭的文件.
將被忽略,並且名稱以 開頭的文件-
將被解釋為選項find
並造成嚴重破壞,因此這不是一般等價於find . …
.
/
檔案名稱中缺少以“”結尾的字串暗示目前目錄,但這並不意味著當前目錄是表示透過空字串(可以說這與缺少字串不同,儘管列印時看起來可能相同)。
答案3
一般來說,空字串並不表示目前目錄,無論是對於 shell 指令還是系統呼叫都是如此。它在一些較舊的系統上有效,但在符合 POSIX 的系統上無效。
有時,當您傳遞空字串並且程式需要目錄名稱時,您會發現程式使用目前目錄。這有時是故意的,有時是當給定字串不以斜線開頭時在當前目錄的絕對路徑前面添加的副作用。
對你來說最好的事情就是離開./
。它沒有任何害處。
如果文件列表是
find . … | sed 's!^\./!!'
請注意,這會破壞一些包含換行符的檔案名稱。這對於人類消費來說通常不是問題,並且 的輸出find
不適合程式消費,因為它是不明確的。如果您使用的-print0
是適合程式使用的 ,那麼您可能並不關心前綴./
。
您可以使用find * …
代替find . …
,但請注意,它find *
有許多缺陷,通常不適合:
.
被省略。- 所有點檔案(名稱以 或 ..` 開頭的檔案
.
)都將被省略。 - 如果目前目錄中有一個檔案名稱以
-
(或名稱!
...(
)開頭的檔案名,它將被 解釋為選項或謂詞find
。
如果您的篩選器排除目前目錄,第一點並不重要。對於第二點,您可以使用模式..?* .[!.]* *
來匹配當前目錄中的所有文件,但您需要檢查每個模式是否至少匹配一個文件,如果不匹配則忽略它。這是可能的,但非常麻煩。最後一點是一個塞子。因此find *
可能適合快速命令列使用,但不要在腳本中使用它。
另一種方法是使用 shell 的遞歸通配工具,例如
printf '%s\n' **/*.h
shopt -s globstar
這需要透過bash 和ksh93來激活set -o globstar
,並且在基本的 POSIX shell(例如 dash)中不存在。預設不會遍歷dot文件;要包含它們,首先使shopt -s dotglob
bash 或FIGNORE='@(.|..)'
ksh93 中的 globbing 不忽略點檔。此外,如果沒有匹配項,則此命令將列印模式;在 bash 中運行shopt -s nullglob
以列印空行,並~(N)**/*.h
在 ksh 中使用該模式。
在 zsh 中,預設會啟用遞歸通配符。使用 glob 限定符D
來包含點文件,N
如果沒有匹配則列印空行(預設情況下,如果模式與任何文件都不匹配,zsh 會拋出錯誤)。您可以printf
像上面那樣使用或
print -rl -- **/*.h(DN)
答案4
您可以使用多種技巧來獲得沒有前導的 find 的輸出./
:
使用 find 的
-printf
選項並告訴它僅列印%f
。看man find
:%f 刪除所有前導目錄的檔案名稱(僅最後一個元素)。
例如:
find . -name "*.h" -printf "%f\n"
解析輸出:
find . -name "*.h" | sed 's#^./##'
-
find * -name "*.h"
至於您的其他問題,空字串從不表示目前目錄。只是各種程式都將當前目錄作為預設目錄,因此當您不帶參數運行它們時,它們將在當前目錄上運行。