Каким кратким способом проверить, что переменные среды заданы в оболочке Unix script?

У меня есть несколько сценариев оболочки 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 должен выходить из системы, если они не установлены.

Ответ 1

Расширение параметров

Очевидным ответом является использование одной из специальных форм разложения параметров:

: ${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. Если параметр равен нулю или не задан, расширение слова (или сообщение на этот счет, если слово отсутствует) записывается в стандартную ошибку, и оболочка, если она не является интерактивной, завершает работу. В противном случае значение параметра будет заменено.

Команда Colon

Я должен, вероятно, добавить, что команда двоеточия просто имеет свои аргументы, а затем успешно. Это оригинальная нотация комментария оболочки (до "#" до конца строки). В течение долгого времени сценарии оболочки Bourne имели двоеточие в качестве первого символа. C Shell читает script и использует первый символ, чтобы определить, была ли она для оболочки C (хеш '#') или оболочки Bourne (двоеточие ":" ). Затем ядро ​​вступило в действие и добавило поддержку "#!/path/to/program", а оболочка Bourne получила комментарии "#", а соглашение с двоеточием прошло на обочине. Но если вы встретите script, который начинается с двоеточия, теперь вы узнаете, почему.


Позиция двойных кавычек

blong в comment:

Любые мысли об этом обсуждении? 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, должно быть исправлено. Я не проверял, что он не возражает против формы, в которой выражение заключено в двойные кавычки, но это разумное предположение, что все будет в порядке.

Ответ 2

Попробуйте следующее:

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;

Ответ 3

Ваш вопрос зависит от используемой оболочки.

Раковина Bourne оставляет очень мало на пути того, что вы после.

НО...

Он работает почти везде.

Просто старайся держаться подальше от чшо. Это было хорошо для добавленных им колоколов и свистков, по сравнению с оболочкой Борна, но сейчас это скрипит. Если вы мне не верите, просто попробуйте и выделите STDERR в csh! (-:

Здесь есть две возможности. Пример выше, а именно:

${MyVariable:=SomeDefault}

в первый раз вам нужно обратиться к $MyVariable. Это берет env. var MyVariable и, если он в данный момент не установлен, присваивает значение SomeDefault переменной для последующего использования.

У вас также есть возможность:

${MyVariable:-SomeDefault}

который просто заменяет SomeDefault для переменной, в которой вы используете эту конструкцию. Он не присваивает значение SomeDefault переменной, а значение MyVariable по-прежнему будет null после того, как этот оператор встретится.

Ответ 4

Несомненно, самый простой подход заключается в том, чтобы добавить переключатель -u в shebang (строка вверху вашего script), предполагая, что вы используете bash:

#!/bin/sh -u

Это приведет к тому, что script будет завершен, если скрываются любые несвязанные переменные.

Ответ 5

${MyVariable:=SomeDefault}

Если MyVariable установлено, а не null, оно будет reset значением переменной (= ничего не происходит).
Else, MyVariable установлен на SomeDefault.

Ответ 6

По-моему, самая простая и совместимая проверка для #!/bin/sh:

if [ "$MYVAR" = "" ]
then
   echo "Does not exist"
else
   echo "Exists"
fi

Опять же, это для /bin/sh и совместимо также с старыми системами Solaris.

Ответ 7

Я всегда использовал:

if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi

Не намного яснее, боюсь.

В CSH у вас есть $? STATE.

Ответ 8

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

Ответ 9

Ни одно из вышеперечисленных решений не работало для моих целей, отчасти потому, что я проверяю среду для открытого списка переменных, которые необходимо установить перед началом длительного процесса. Я закончил с этим:

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}

Ответ 10

Мы можем написать хорошее утверждение, чтобы сразу проверить связку переменных:

#
# 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 "[email protected]"; 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

Ответ 11

Для будущего 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

Ответ 12

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

is_this_an_env_variable ()
    local var="$1"
    if env |grep -q "^$var"; then
       return 0
    else
       return 1
    fi
 }

Ответ 14

Синтаксис $? довольно опрятен:

if [ $?BLAH == 1 ]; then 
    echo "Exists"; 
else 
    echo "Does not exist"; 
fi