Проверьте, установлены ли несколько переменных


19

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

for var in $one $two $three ; do
    ...

но если, например $two, не установлено, цикл никогда не выполняется для $two. Следующее, что я попробовал, было

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Но если два не установлено, я все равно получаю «два установлено в» вместо «два не установлено».

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

Обновление / решение: я знаю, что есть разница между «набором» и «набором, но пустым». Теперь я использую (спасибо /programming//a/16753536/3456281 и ответы на этот вопрос) следующее:

if [ -n "${!var:-}" ] ; then

поэтому, если varустановлено, но пусто, оно все еще считается недействительным.


1
Вы также можете добавить set -uв начало вашего скрипта, чтобы немедленно завершить его, когда используется неустановленная переменная.
n.st

Ответы:


8

Ошибка цитирования.

if [ -n "${!var}" ] ; then

На будущее: настройка

set -x

перед запуском кода показал бы вам проблему. Вместо того, чтобы добавить это к коду, вы можете вызвать скрипт

bash -vx ./my/script.sh

Это работает, но что происходит с / без кавычек? Правильный ли мой общий подход в первую очередь?
Джаспер

Да, это предвидение: я ответил на вопрос, прежде чем он был задан ... 8-)
Hauke ​​Laging

4
@Jasper Вы всегда должны заключать в кавычки переменные. Это не стоит больше времени, чем размышления о том, необходимо ли это каждый раз. Но это позволяет избежать ошибок.
Хауке Лагинг

Напротив, цитирование только защищает от расширения. Если расширение - это то, что вас интересует, цитирование, безусловно, не тот путь. Например: cs = 673 290 765; set - $ (IFS =,; echo $ cs); echo $ #; выходной: 3
mikeserv

2
@mikeserv Только одиночные кавычки защищают от расширения, что я, очевидно, не предлагаю. Двойные кавычки защищают от разбиения слов. Я не отрицал, что могут быть случаи, когда цитирование должно быть опущено. Но это не аргумент против моего общего правила. Какие люди собираются использовать что-то вроде set -- $(IFS=, ; echo $cs)? Тип людей, которые должны спросить здесь, почему if [ -n ${!var} ] ; thenне работает? Возможно нет.
Хауке Лагинг

5

Единственное, что вам нужно, это кавычки в вашем тесте:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Работает для меня.


4

Если вы хотите, чтобы программа остановилась:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

Это сработает. Каждое из сообщений, следующих за знаком вопроса, печатается, stderrи родительская оболочка умирает. Хорошо, хорошо, так что не каждое сообщение - только одно - только первое, которое терпит неудачу, печатает сообщение, потому что оболочка умирает. Мне нравится использовать эти тесты, как это:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

У меня было намного больше, чтобы сказать об этом здесь .


2

Можете добавить

set -u

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

Сценарий как

#!/bin/sh
set -u
echo $foo

приведет к

script.sh: 3: script.sh: foo: параметр не установлен

Если вы используете bashвместо этого, ошибка будет выглядеть следующим образом:

script.sh: строка 3: foo: несвязанная переменная


И, на ваш взгляд, в чем смысл проверок во время выполнения?
Хауке Лагинг

2
@HaukeLaging Я не совсем следую за вами - set -uпредотвращает именно ту ошибку, которую пытается избежать ОП, и (в отличие от всех других решений) не ограничивается конкретным набором переменных. На самом деле, это практически полезная мера предосторожности для почти всех сценариев оболочки, чтобы они могли безопасно завершать работу, вместо того чтобы делать неожиданные действия, когда переменная не установлена. Эта статья является удобным справочником по теме.
n.st

@ n.st Я не согласен - значение null может быть столь же полезным, как и значение null, если вы планируете это.
mikeserv

1
@ n.st Это не имеет никакого смысла для ОП, так как он не хочет защиты от доступа к неустановленным переменным (кстати: установленная, но пустая переменная вызовет ту же ошибку, но не отреагирует на вашу «защиту»). Он хочет проверить во время выполнения, является ли переменная неустановленной / пустой. Ваше предложение может быть полезным в качестве общей помощи при разработке, но не решает проблему ОП.
Хауке Лагинг

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

2

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

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

Я использую переменную массива для отслеживания того, какие переменные не были установлены, и использую результат для составления сообщения, обращенного к пользователю.

Примечания:

  • Я цитировал ${required_vars[@]}в forцикле в основном по привычке - я бы не советовал включать метасимволы оболочки в имена переменных!
  • Я не цитировал ${#missing_vars[@]}, потому что это всегда целое число, даже если вы достаточно извращены, чтобы игнорировать предыдущий совет.
  • Я использовал %qпри печати; %sобычно будет достаточно.
  • Вывод ошибок всегда идет в поток ошибок с >&2, поэтому он не попадает в подчиненные команды
  • Тишина золотая - не печатайте информацию о ходе выполнения или отладочную информацию, если только об этом специально не спрашивают. Это делает ошибки более очевидными.

1

bash4.2 позволяет проверить, установлена ​​ли переменная с -vоператором; переменная unset и переменная, установленная в пустую строку, являются двумя различными условиями:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

Я спрашивал о «множественных» переменных, поэтому я искал что-то вроде косвенного обращения ...
Джаспер

Вам не нужно окольные, так как -vпринимает имя переменной, так for var in one two three; [[ -v $var ]] && ...; doneчто проверить , если каждый из one, twoи threeустанавливаются в последовательности.
chepner

1

Я думаю, если вы имеете в виду not set, поэтому переменная никогда не должна быть инициализирована. Если вы используете [ -n "${!var}" ], то пустая переменная вроде two=""будет сбой, пока она установлена . Вы можете попробовать это:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

Краткое (хотя и немного хакерское) решение, позволяющее справиться со случаем, когда sourceсценарию d разрешено устанавливать переменные в нулевое значение, состоит в том, чтобы инициализировать переменные каким-либо специальным значением перед поиском сценария, а затем проверить это значение впоследствии. например,

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
другое хорошее решение, но я хотел бы дать более конкретное сообщение об ошибке, чем «одна или несколько переменных не установлены» ...
Джаспер

0

Я расширил ответ @ mikeserv .

Эта версия принимает список переменных, чтобы проверить наличие, печатая имя отсутствующей переменной и вызывая функцию using () при ошибке.

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Пример вывода, когда значение отсутствует:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.