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

dirnamebasenamereadlink(感谢@Marco - 这已得到纠正)当安全性变得重要时(需要路径的安全性)可能会产生可移植性问题。许多系统(如 Fedora Linux)将其放置/bin/usr/bin.然后是 Windows 上的 Bash,例如 cygwin、msys 等。 如果可能的话,保持纯粹的 Bash 总是更好。(根据@Marco 评论)

顺便说一句,感谢您指向 shellcheck 的指针,我以前没有见过。

相关内容