答案1
這與效率無關——而是與正確性有關。basename
使用換行符號來分隔它列印出來的檔案名稱。在通常情況下,當您只傳遞一個檔案名稱時,它會在其輸出中添加一個尾隨換行符。由於檔案名稱本身可能包含換行符,因此很難正確處理這些檔案名稱。
basename
人們通常會這樣使用這一事實,這使得情況變得更加複雜: "$(basename "$file")"
。這使得事情變得更加困難,$(command)
因為全部尾隨換行符來自command
.考慮$file
以換行符結尾的不太可能的情況。然後basename
將添加一個額外的換行符,但"$(basename "$file")"
會刪除兩個都換行符,留下不正確的檔名。
另一個問題是basename
,如果$file
以 a(破折號又稱為減號)開頭-
,它將被解釋為選項。這個很容易修復:$(basename -- "$file")
穩健的使用方式basename
是這樣的:
# A file with three trailing newlines.
file=$'/tmp/evil\n\n\n'
# Add an 'x' so we can tell where $file's newlines end and basename's begin.
file_x="$(basename -- "$file"; printf x)"
# Strip off two trailing characters: the 'x' added by us and the newline added by basename.
base="${file_x%??}"
另一種方法是使用${file##*/}
,它更簡單,但也有自己的錯誤。特別是,在$file
is/
或 的情況下是錯誤的foo/
。
答案2
shellcheck
中的相關行原始碼是:
checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "dirname" =
style id "Use parameter expansion instead, such as ${var%/*}."
checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "basename" =
style id "Use parameter expansion instead, such as ${var##*/}."
checkNeedlessCommands _ = return ()
沒有明確給出解釋,但根據函數的名稱 ( checkNeedlessCommands
),看起來 @jordanm 是非常正確的,它建議您避免分叉一個新進程。
答案3
dirname
、basename
等等readlink
(感謝@Marco - 這已得到糾正)當安全性變得重要時(需要路徑的安全性)可能會產生可移植性問題。許多系統(如 Fedora Linux)將其放置/bin
在/usr/bin
.然後是 Windows 上的 Bash,例如 cygwin、msys 等。 如果可能的話,保持純粹的 Bash 總是更好。(根據@Marco 評論)
順便說一句,感謝您指向 shellcheck 的指針,我以前從未見過。