shellcheck 建議不要使用基本名稱:為什麼?

shellcheck 建議不要使用基本名稱:為什麼?

我正在嘗試外殼檢查

我有類似的東西

basename "${OPENSSL}" 

我得到以下建議

Use parameter expansion instead, such as ${var##*/}.

從實用的角度來看,我認為沒有什麼差別

$ export OPENSSL=/opt/local/bin/openssl
$ basename ${OPENSSL}
openssl
$ echo ${OPENSSL##*/}
openssl

由於basename是在POSIX 規範,我不知道為什麼它應該是最佳實踐。有什麼提示嗎?

答案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##*/},它更簡單,但也有自己的錯誤。特別是,在$fileis/或 的情況下是錯誤的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

dirnamebasename等等readlink(感謝@Marco - 這已得到糾正)當安全性變得重要時(需要路徑的安全性)可能會產生可移植性問題。許多系統(如 Fedora Linux)將其放置/bin/usr/bin.然後是 Windows 上的 Bash,例如 cygwin、msys 等。 如果可能的話,保持純粹的 Bash 總是更好。(根據@Marco 評論)

順便說一句,感謝您指向 shellcheck 的指針,我以前從未見過。

相關內容