Есть ли какой-либо вред в использовании неустановленных переменных?

Есть ли какой-либо вред в использовании неустановленных переменных?

Допустим, у меня есть следующий код:

# Check if the color prompt is enabled and supported on this system
if [ -n "$force_color_prompt" ] && [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    GREEN="\033[1;32m"
    DIM="\033[2m"
    RESET="\033[00m"
fi

echo -e "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

Если включена поддержка цвета, то будет выведена красивая цветная линия. Если поддержка цвета не включена, то значения типа ${GREEN}не будут установлены, и текст будет напечатан обычным белым цветом на черном фоне.

Код полагается на тот факт, что переменные, которые не установлены, просто будут оцениваться как пустая строка (что в моих тестах и ​​происходит). Вызовет ли это ошибки или проблемы в некоторых системах, или все несуществующие переменныевсегдавычислить пустую строку? Есть ли причина, по которой мне не следует полагаться на эту механику?

решение1

Несуществующие переменные всегда будут возвращать пустую строку при раскрытии с помощью $FOOили (эквивалентно) ${FOO}, и нет ничего плохого в том, чтобы зависеть от этого, за исключением одного конкретного случая:

Если кто-то вызвал set -uтекущую оболочку до того, как вы попытались использовать эту переменную, значит, он включил этот параметр:

              -u Рассматривать неустановленные переменные как ошибку при выполнении параметров.
                      расширение eter. Если расширение будет предпринято на неустановленном
                      переменная, оболочка выводит сообщение об ошибке, и, если нет
                      интерактивный, завершается с ненулевым статусом.

Это означает, что если вы пишете функцию, предназначенную для использования в скрипте, которым управляет кто-то другой, вам, возможно, придется проявить паранойю по поводу использования неустановленных переменных. В противном случае, если они использовали их set -uдо вызова вашей функции, их скрипт завершится с сообщением об ошибке при первой попытке раскрыть неустановленную переменную.

Если вы пишете собственный скрипт, нет ничего плохого в том, чтобы рассчитывать на то, что неустановленные переменные будут расширяться до пустой строки.

РЕДАКТИРОВАТЬ- И еще, просто мысль - поскольку вы делаете все это в зависимости от того, доступны ли возможности цвета terminfo для вашего терминала, почему бы на самом деле не использовать terminfo для генерации последовательностей, а не жестко кодировать значения vt100? Что-то вроде:

if [ -n "$force_color_prompt" ] && type tput &>/dev/null; then
    GREEN="$(tput setaf 2)$(tput bold)"
    DIM="$(tput dim)"
    RESET="$(tput sgr0)"
fi

Это может дать вам некоторую переносимость на другие терминалы (хотя, надо признать, что количество терминалов, которые не используют указанные вами коды, невелико и сокращается). Это также может привести к потере некоторой переносимости, поскольку некоторые возможности могут отсутствовать на некоторых платформах в зависимости от того, насколько корректны определения terminfo. YMMV.

решение2

Одной из самых уникальных особенностей языка сценариев оболочки, совместимого с POSIX, являетсяпараметр-расширение. Его можно использовать различными способами для выполнения задач, которые обычно не ассоциируются со значениями переменных. В оболочке переменная может быть чем-то большим, чем просто значение — она может быть элементом действия. Она может сама себя тестировать. И это происходит явно — без необходимости устанавливать параметры оболочки.

Например, ваш код может выглядеть так:

N= ERR='error encountered - exiting' 
: ${force_color_prompt?"$ERR"}
/usr/bin/tput setaf >/dev/null 2>&1 || ${N:?"$ERR"}
: "${GREEN:=\033[1;32m}" "${DIM:=\033[2m}" "${RESET:=\033[00m}"
printf %b\\n \
    "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

Переменная $Nявно установлена ​​в пустую строку, и поэтому при ее оценке с ${N:?}формой расширения параметров ее родительская оболочка автоматически завершается, а следующий за ней оператор ?также оценивается для расширения, результаты которого выводятся на stderr. То же самое касается $force_color_prompt- если она не установлена, то скрипт автоматически завершается с ошибкой и выводится $ERRна stderr- all.

И устанавливаются в значения $GREEN $RESET, $DIMкоторые вы определили, если они в данный момент не установлены или установлены в ''пустую строку. Это позволяет вам передавать их значения в скрипт как переменные среды. Например, если приведенный выше фрагмент был в скрипте, который называется greenworld.shи я вызвал его так:

GREEN="$(tput setaf 2)$(tput bold)" greenworld.sh

Тогда $GREENне будет сброшено содержимое скрипта, а вместо этого унаследует явное значение, которое я для него установил. Это делает скрипты оболочки гибкими.

И использование tputтаким образом, как рекомендовал godlygeek, является рекомендацией I, во-первых, во-вторых.

В оболочке неустановленная переменная иногда может быть столь же полезна, как и установленная. Вот другой пример:

set -- * 
while ${1+:} false ; do
    #do stuff until all positionals are shifted away
shift ; done

В этом примере, пока первый параметр определен, он будет расширяться до встроенного :null оболочки, что, следовательно, сделает следующий falseвызов пустым. Но как только все позиционные параметры будут shiftудалены, ${1}он не будет расширяться таким образом, и falseбудет вызван, и whileцикл закончится. Вы можете сделать бесчисленное количество вариаций этого.

решение3

При обычном использовании, т. е. когда вы просто расширяете переменные, неустановленная переменная рассматривается как пустая во всех оболочках в стиле Bourne/POSIX. Исключением является случай, когда действует set -o unsetaka set -u, в этом случае оболочка выдаст ошибку, если вы попытаетесь получить доступ к значению неустановленной переменной (см.ответ godlygeek).

Существуют способы проверить, является ли переменная неустановленной или пустой; например, конструкция ${foo-bar}расширяется до значения , fooесли она установлена ​​(даже если она пуста), и до , barесли fooона не установлена;
${foo:-bar}(с дополнительным двоеточием) обрабатывает пустую переменную так, как если бы она была неустановленной. Другие похожие конструкции расширения ${foo+bar}, ${foo?bar}, ${foo=bar}ведут себя аналогично. Пустая переменная также появится в выводе setи export(если она экспортирована), среди прочих различий. Вы столкнетесь с этими различиями, только если захотите.

Однако есть и другая причинавсегда инициализируйте переменные: как узнать, что переменная действительно не установлена? Вызывающий объект мог определить похожую переменную для своих собственных целей. Если вызывающий объект является какой-то другой частью того же скрипта, то ваше использование в функции перезапишет переменную вызывающего объекта, если только вы не объявите переменную локальной (что возможно только в ksh/bash/zsh), поэтому конфликты имен переменных в любом случае являются проблемой. Но также может случиться, что переменная присутствует в среде, потому что кто-то другой выбрал то же имя переменной, что и вы. Существует своего рода соглашение использовать строчные имена для переменных оболочки и заглавные имена для переменных среды, но это не решает всех конфликтов и не соблюдается повсеместно.

$ экспорт DIM=1 SUM=42
$ баш
О, зеленый мир, не покидай меня сейчас...

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