У меня есть несколько сценариев оболочки Unix, где мне нужно проверить, что определенные переменные среды установлены до того, как я начну делать вещи, поэтому я делаю такие вещи:
if [ -z "$STATE" ]; then
echo "Need to set STATE"
exit 1
fi
if [ -z "$DEST" ]; then
echo "Need to set DEST"
exit 1
fi
который много печатает. Есть ли более элегантная идиома для проверки того, установлен ли набор переменных среды?
EDIT: я должен упомянуть, что эти переменные не имеют значимого значения по умолчанию - script должен выходить из системы, если они не установлены.
Очевидным ответом является использование одной из специальных форм разложения параметров:
: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}
Или, лучше (см. раздел "Положение двойных кавычек" ниже):
: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"
Первый вариант (используя только ?
) требует установки STATE, но STATE = "" (пустая строка) - ОК — не совсем то, что вы хотите, но альтернативное и более раннее обозначение.
Второй вариант (с использованием :?
) требует установки DEST и непустого.
Если вы не передаете сообщение, оболочка предоставляет сообщение по умолчанию.
Конструкция ${var?}
переносится обратно в Версию 7 UNIX и Bourne Shell (1978 или около того). Конструкция ${var:?}
немного более поздняя: я думаю, что это было в System III UNIX около 1981 года, но, возможно, это было в PWB UNIX до этого. Поэтому он находится в оболочке Korn и в оболочках POSIX, включая, в частности, Bash.
Обычно это документируется на странице руководства оболочки в разделе Расширение параметров. Например, в руководстве bash
говорится:
${parameter:?word}
Отобразить ошибку, если Null или Unset. Если параметр равен нулю или не задан, расширение слова (или сообщение на этот счет, если слово отсутствует) записывается в стандартную ошибку, и оболочка, если она не является интерактивной, завершает работу. В противном случае значение параметра будет заменено.
Я должен, вероятно, добавить, что команда двоеточия просто имеет свои аргументы, а затем успешно. Это оригинальная нотация комментария оболочки (до "#
" до конца строки). В течение долгого времени сценарии оболочки Bourne имели двоеточие в качестве первого символа. C Shell читает script и использует первый символ, чтобы определить, была ли она для оболочки C (хеш '#
') или оболочки Bourne (двоеточие ":
" ). Затем ядро вступило в действие и добавило поддержку "#!/path/to/program
", а оболочка Bourne получила комментарии "#
", а соглашение с двоеточием прошло на обочине. Но если вы встретите script, который начинается с двоеточия, теперь вы узнаете, почему.
Любые мысли об этом обсуждении? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749
Суть обсуждения такова:
... Однако, когда я
shellcheck
он (с версией 0.4.1), я получаю это сообщение:In script.sh line 13: : ${FOO:?"The environment variable 'FOO' must be set and non-empty"} ^-- SC2086: Double quote to prevent globbing and word splitting.
Любые советы о том, что я должен делать в этом случае?
Короткий ответ: "do as shellcheck
предлагает":
: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"
Чтобы проиллюстрировать, почему, изучите следующее. Обратите внимание, что команда :
не отвечает на свои аргументы (но оболочка действительно оценивает аргументы). Мы хотим видеть аргументы, поэтому нижеприведенный код использует printf "%s\n"
вместо :
.
$ mkdir junk
$ cd junk
$ > abc
$ > def
$ > ghi
$
$ x="*"
$ printf "%s\n" ${x:?You must set x} # Careless; not recommended
abc
def
ghi
$ unset x
$ printf "%s\n" ${x:?You must set x} # Careless; not recommended
bash: x: You must set x
$ printf "%s\n" "${x:?You must set x}" # Careful: should be used
bash: x: You must set x
$ x="*"
$ printf "%s\n" "${x:?You must set x}" # Careful: should be used
*
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
abc
def
ghi
$ x=
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
bash: x: You must set x
$ unset x
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
bash: x: You must set x
$
Обратите внимание, как значение в $x
раскрывается до первого *
, а затем список имен файлов, когда общее выражение не находится в двойных кавычках. Это то, что рекомендуется рекомендовать shellcheck
, должно быть исправлено. Я не проверял, что он не возражает против формы, в которой выражение заключено в двойные кавычки, но это разумное предположение, что все будет в порядке.
Попробуйте следующее:
[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;
[ -z "$STATE" ] && { echo "Need to set STATE"; exit 1; }
см. этот блог
Ваш вопрос зависит от используемой оболочки.
Раковина Bourne оставляет очень мало на пути того, что вы после.
НО...
Он работает почти везде.
Просто старайся держаться подальше от чшо. Это было хорошо для добавленных им колоколов и свистков, по сравнению с оболочкой Борна, но сейчас это скрипит. Если вы мне не верите, просто попробуйте и выделите STDERR в csh! (-:
Здесь есть две возможности. Пример выше, а именно:
${MyVariable:=SomeDefault}
в первый раз вам нужно обратиться к $MyVariable. Это берет env. var MyVariable и, если он в данный момент не установлен, присваивает значение SomeDefault переменной для последующего использования.
У вас также есть возможность:
${MyVariable:-SomeDefault}
который просто заменяет SomeDefault для переменной, в которой вы используете эту конструкцию. Он не присваивает значение SomeDefault переменной, а значение MyVariable по-прежнему будет null после того, как этот оператор встретится.
Несомненно, самый простой подход заключается в том, чтобы добавить переключатель -u
в shebang (строка вверху вашего script), предполагая, что вы используете bash
:
#!/bin/sh -u
Это приведет к тому, что script будет завершен, если скрываются любые несвязанные переменные.
set -u
${MyVariable:=SomeDefault}
Если MyVariable
установлено, а не null, оно будет reset значением переменной (= ничего не происходит).
Else, MyVariable
установлен на SomeDefault
.
По-моему, самая простая и совместимая проверка для #!/bin/sh:
if [ "$MYVAR" = "" ]
then
echo "Does not exist"
else
echo "Exists"
fi
Опять же, это для /bin/sh и совместимо также с старыми системами Solaris.
/bin/sh
который поддерживает нотацию ${var:?}
и т. Д.
Я всегда использовал:
if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi
Не намного яснее, боюсь.
В CSH у вас есть $? STATE.
STATE
пустым или неустановленным, а не просто неустановленным.
bash
4.2 представил оператор -v
, который проверяет, установлено ли имя для любого значения, даже пустая строка.
$ unset a
$ b=
$ c=
$ [[ -v a ]] && echo "a is set"
$ [[ -v b ]] && echo "b is set"
b is set
$ [[ -v c ]] && echo "c is set"
c is set
Ни одно из вышеперечисленных решений не работало для моих целей, отчасти потому, что я проверяю среду для открытого списка переменных, которые необходимо установить перед началом длительного процесса. Я закончил с этим:
mapfile -t arr < variables.txt
EXITCODE=0
for i in "${arr[@]}"
do
ISSET=$(env | grep ^${i}= | wc -l)
if [ "${ISSET}" = "0" ];
then
EXITCODE=-1
echo "ENV variable $i is required."
fi
done
exit ${EXITCODE}
Вместо использования внешних сценариев оболочки я обычно загружаю функции в свой логин. Я использую что-то вроде этого как вспомогательную функцию для проверки переменных окружения, а не любой заданной переменной:
is_this_an_env_variable ()
local var="$1"
if env |grep -q "^$var"; then
return 0
else
return 1
fi
}
is_this_an_env_variable P
вернет успех, потому что PATH
существует.
Для будущего poeple, подобного мне, я хотел сделать шаг вперед и параметризовать имя var, поэтому я могу перебрать список переменных с переменными размерами:
#!/bin/bash
declare -a vars=(NAME GITLAB_URL GITLAB_TOKEN)
for var_name in "${vars[@]}"
do
if [ -z "$(eval "echo \$$var_name")" ]; then
echo "Missing environment variable $var_name"
exit 1
fi
done
Мы можем написать хорошее утверждение, чтобы сразу проверить связку переменных:
#
# assert if variables are set (to a non-empty string)
# if any variable is not set, exit 1 (when -f option is set) or return 1 otherwise
#
# Usage: assert_not_null [-f] variable ...
#
function assert_not_null() {
local fatal var num_null=0
[[ "$1" = "-f" ]] && { shift; fatal=1; }
for var in "$@"; do
[[ -z "${!var}" ]] &&
printf '%s\n' "Variable '$var' not set" >&2 &&
((num_null++))
done
if ((num_null > 0)); then
[[ "$fatal" ]] && exit 1
return 1
fi
return 0
}
Образец вызова:
one=1 two=2
assert_not_null one two
echo test 1: return_code=$?
assert_not_null one two three
echo test 2: return_code=$?
assert_not_null -f one two three
echo test 3: return_code=$? # this code shouldn't execute
Вывод:
test 1: return_code=0
Variable 'three' not set
test 2: return_code=1
Variable 'three' not set
Это тоже может быть так:
if (set -u; : $HOME) 2> /dev/null
...
...
http://unstableme.blogspot.com/2007/02/checks-whether-envvar-is-set-or-not.html
Синтаксис $?
довольно опрятен:
if [ $?BLAH == 1 ]; then
echo "Exists";
else
echo "Does not exist";
fi