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начинается с -(тире, то есть минуса), то это будет интерпретировано как опция. Это легко исправить:$(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, я его раньше не видел.

Связанный контент