Какой краткий способ проверить, что переменные среды установлены в сценарии оболочки Unix? - PullRequest
471 голосов
/ 21 ноября 2008

У меня есть несколько сценариев оболочки 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

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

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

Ответы [ 14 ]

558 голосов
/ 21 ноября 2008

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

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

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

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

Если вы не укажете сообщение, оболочка предоставит сообщение по умолчанию.

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

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

${parameter:?word}

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

Колонное командование

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


Положение двойных кавычек

blong спросил в комментарии :

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

123 голосов
/ 21 ноября 2008

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

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;
55 голосов
/ 21 ноября 2008

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

Оболочка Борна оставляет очень мало того, что вы ищете.

НО ...

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

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

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

${MyVariable:=SomeDefault}

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

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

${MyVariable:-SomeDefault}

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

47 голосов
/ 26 октября 2011

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

#!/bin/sh -u

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

37 голосов
/ 21 ноября 2008
${MyVariable:=SomeDefault}

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

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

MyVariable=${MyVariable:=SomeDefault}
20 голосов
/ 11 октября 2014

На мой взгляд, самая простая и наиболее совместимая проверка для #! / Bin / sh :

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

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

15 голосов
/ 21 сентября 2016

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
14 голосов
/ 21 ноября 2008

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

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

Боюсь, не так уж и кратко.

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

3 голосов
/ 22 июня 2017

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

#!/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
2 голосов
/ 16 мая 2017

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

#
# 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
  [[ "$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_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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...