Я пробуюпроверка оболочки.
У меня есть что-то подобное
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
начинается с -
(тире, то есть минуса), то это будет интерпретировано как опция. Это легко исправить:$(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
есть /
или foo/
.
решение2
Соответствующие строки в shellcheck
'sисходный кодявляются:
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
тогда как другие (например, Mac OSX) помещают его в /usr/bin
. Затем есть Bash в Windows, например, cygwin, msys и другие. Всегда лучше оставаться чистым Bash, когда это возможно.(согласно комментарию @Marco)
Кстати, спасибо за ссылку на shellcheck, я его раньше не видел.