Допустим, у меня есть следующий код:
# 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 unset
aka 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 $ баш О, зеленый мир, не покидай меня сейчас...