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

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

14 ответов


Расширения Параметр

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

: ${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="" (пустая строка) в порядке - не совсем то, что вы хотите, но альтернативная и более старая нотация.

второй вариант (используя :?) требует, чтобы DEST был набор и непустой.

если сообщение Не указано, оболочка предоставляет сообщение по умолчанию.

на ${var?} construct переносится обратно в версию 7 UNIX и оболочку Bourne (1978 или около того). The ${var:?} конструкция немного более поздняя: я думаю, что это было в System III UNIX около 1981 года, но, возможно, это было в PWB UNIX до этого. Поэтому он находится в оболочке Korn и в оболочках POSIX, включая конкретно Bash.

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

${parameter:?word}

ошибка отображения, если Null или Unset. Если параметр равен null или unset, расширение word (или сообщение об этом, если word отсутствует) записывается в стандартную ошибку, и оболочка, если она не является интерактивной, завершает работу. В противном случае подставляется значение параметра.

Двоеточие Команда

я, вероятно, должен добавить, что команда двоеточия просто оценивает свои аргументы,а затем успешно. Это оригинальная нотация комментария оболочки (до '#' до конца строки). Долгое время сценарии Bourne shell имели двоеточие в качестве первого символа. Оболочка C будет читать сценарий и использовать первый символ, чтобы определить, является ли он для оболочки C (a '#' хэш) или оболочка Борна (a ':' двоеточие). Затем ядро вступило в игру и добавило: поддержка '' комментарии, и Конвенция колона отошла на второй план. Но если вы встретите сценарий, который начинается с двоеточия, теперь вы узнаете, почему.


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

белокурая спросил комментарий:

есть мысли по этому поводу? 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.

любые советы о том, что я должен делать в этом случае?

короткий ответ: "Делай, как 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 расширяется до first * а затем список имен файлов, когда общее выражение не находится в двойных кавычках. Это shellcheck рекомендуется исправить. Я не проверил, что он не возражает против формы, в которую вложено выражение в двойных кавычках, но это разумное предположение, что все будет в порядке.


попробуйте это:

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

на ваш вопрос зависит от оболочки, которую вы используете.

Bourne shell оставляет очень мало на пути того, что вам нужно.

но...

это работает, почти везде.

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

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

${MyVariable:=SomeDefault}

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

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

${MyVariable:-SomeDefault}

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


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

#!/bin/sh -u

это приведет к выходу скрипта, если какие-либо несвязанные переменные скрываются внутри.


${MyVariable:=SomeDefault}

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

вышеуказанное попытается выполнить ${MyVariable}, так что если вы просто хотите установить переменную:

MyVariable=${MyVariable:=SomeDefault}

на мой взгляд самая простая и совместимая проверка за #!/ bin / sh - это:

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

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


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

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

боюсь, не намного более лаконично.

под CSH у вас есть $?ГОСУДАРСТВО.


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

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

#
# 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_var_not_null [-f] variable ...
#
function assert_var_not_null() {
  local fatal var num_null=0
  [[ "" = "-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_var_not_null one two
echo test 1: return_code=$?
assert_var_not_null one two three
echo test 2: return_code=$?
assert_var_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

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

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}

для будущих людей, таких как я, я хотел сделать шаг вперед и параметризовать имя 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

Это тоже может быть:

if (set -u; : $HOME) 2> /dev/null
...
...

http://unstableme.blogspot.com/2007/02/checks-whether-envvar-is-set-or-not.html


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

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

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

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