Вопрос или проблема
Я пишу bash-скрипт, и моя IDE жалуется на следующие объявления:
local grey=$(tput setaf 154)
local red=$(tput setaf 196)
local bold=$(tput bold)
local default=$(tput sgr0)
Она говорит следующее:
Объявляйте и назначайте отдельно, чтобы избежать маскирования значений возврата.
Смотрите SC2155.
Я понимаю, что значит это предупреждение, и я могу избавиться от него, но мой финальный код выглядит очень некрасиво.
Я могу подавить предупреждения следующим образом:
# shellcheck disable=SC2155
local grey=$(tput setaf 154)
# shellcheck disable=SC2155
local red=$(tput setaf 196)
# shellcheck disable=SC2155
local bold=$(tput bold)
# shellcheck disable=SC2155
local default=$(tput sgr0)
Или я могу разделить объявление и присвоение значений следующим образом:
local grey
grey=$(tput setaf 154)
local red
red=$(tput setaf 196)
local bold
bold=$(tput bold)
local default
default=$(tput sgr0)
Решения выше выглядят слишком многословно.
Я также могу сделать это:
local grey; grey=$(tput setaf 154)
local red; red=$(tput setaf 196)
local bold; bold=$(tput bold)
local default; default=$(tput sgr0)
Но я не уверен, какой способ является наилучшим решением.
Может быть, я могу проигнорировать это предупреждение, потому что мой код в порядке. Не уверен, как правильно написать чистый bash-скрипт, который соответствует стандартам.
Я не думаю, что существует единственный ответ для “лучшей практики”. Как бы я написал это:
local grey red bold default
grey=$(tput setaf 154)
red=$(tput setaf 196)
bold=$(tput bold)
default=$(tput sgr0)
Причина, по которой нельзя комбинировать объявление, как local
, и присвоение с использованием подстановки команд, заключается в том, что статус var=$(somecommand)
— это код возврата somecommand
, но статус local …
всегда 0. Таким образом, local var=$(somecommand)
скрывает любые ошибки из somecommand
. По этой же причине не следует использовать несколько подстановок команд в одном присвоении.
Это, конечно, имеет значение, если вы действительно обращаете внимание на статус команды, либо проверяя $?
, либо включая set -e
.
Вы можете решить игнорировать это для команд, которые вряд ли завершатся с ошибкой, например, destination_file=$(dirname -- "$other_file")/$(basename -- "$source_file")
. tput
не относится к таким командам: он выдаст ошибку, если скрипт запущен в терминале, где отсутствует требуемая возможность.
Еще одной причиной не сочетать объявление с присвоением является то, что это работает только для скалярных переменных, а не для массивов. Комбинирование объявления с литеральной строкой или числом (local filename="hello.txt"
, local -i n=3
) в порядке.
Другой возможностью является использование опции -r
(только для чтения) для local
:
local -r grey=$(tput setaf 154)
local -r red=$(tput setaf 196)
local -r bold=$(tput bold)
local -r default=$(tput sgr0)
Shellcheck не будет жаловаться на это, потому что переменные, доступные только для чтения, должны быть присвоены при объявлении. Конечно, это не решает основную причину SC2155, т.е. значения возврата все равно поглощаются, так что это, вероятно, нельзя назвать “лучшей практикой”. Но если вы все равно собираетесь игнорировать значения возврата, это не имеет значения.
SpellCheck теперь позволяет глобальное # shellcheck disable=SC2155
в начале. <— используйте это. или
local var1; var1=q
local var2; var2=u
(аналогично синтаксису обычных for
циклов).
local var1, var2
не портируемо (я думал, что это этот пост перечисляет консоли, которые поддерживают или не поддерживают это, но сейчас не уверен… это большой пост…).
Невозможно использовать local -r
в Termux, поэтому буду считать, что многие системы этого не поддерживают:
~ $ cat ./u
#!/bin/sh
u() {
local -r q
}
u
$ ./u
./u: 3: local: -r: неверное имя переменной
.
Ответ или решение
Лучшие практики для SC2155: Разделение объявления и присвоения переменной в скриптах Bash
При создании Bash-скриптов, важно следовать рекомендациям и стандартам, чтобы обеспечить надёжность и читаемость кода. Проблема, с которой вы столкнулись, связана с предупреждением от ShellCheck под идентификатором SC2155: «Объявляйте и присваивайте значения переменным отдельно, чтобы не маскировать возвращаемые значения». Давайте подробно рассмотрим, как правильно и профессионально оформить код, избегая подобных предупреждений.
Понимание проблемы SC2155
Когда вы объединяете объявление переменной и командную подстановку, как например в строке local grey=$(tput setaf 154)
, ShellCheck предупреждает вас, потому что такие конструкции могут скрывать коды возврата выполняемых команд. В случае, если tput
вернёт ненулевой код ошибки, это может остаться незамеченным, поскольку команда local
всегда возвращает 0.
Рекомендованные способы решения проблемы
-
Отделить объявление и присвоение:
local grey grey=$(tput setaf 154) local red red=$(tput setaf 196) local bold bold=$(tput bold) local default default=$(tput sgr0)
Такой подход предотвращает скрытие ошибок и делает код более прозрачным.
-
Объединение без упрощения:
local grey; grey=$(tput setaf 154) local red; red=$(tput setaf 196) local bold; bold=$(tput bold) local default; default=$(tput sgr0)
Данный метод сохраняет компактность кода, сохраняя при этом его корректность.
-
Объявление нескольких переменных с последующим присвоением:
local grey red bold default grey=$(tput setaf 154) red=$(tput setaf 196) bold=$(tput bold) default=$(tput sgr0)
Это позволяет сначала объявить все переменные, а затем присвоить им значения, сводя код к более структурированному виду.
Дополнительные аспекты и ограничения:
-
Использование опции
-r
дляlocal
, чтобы сделать переменные доступными только для чтения:local -r grey=$(tput setaf 154)
Однако, такой подход не решает проблемы с возвратными кодами, поэтому применять его следует с осторожностью.
-
В некоторых системах, например, Termux, может отсутствовать поддержка
local -r
, что может вызвать ошибки при выполнении скриптов.
Выводы и рекомендации
В конечном счёте, выбирайте подход, наиболее соответствующий вашим требованиям и конвенциям команды разработчиков. Разделение объявления и присвоения значений переменных позволяет избежать потенциальных проблем и делает код более гибким и переносимым. Важно следовать стандартам, не только чтобы избежать ошибок, но и для обеспечения читаемости и лёгкости сопровождения кода в будущем.