Перевірте, чи встановлено кілька змінних


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-)
Хоуке Лагінг,

4
@Jasper Ви завжди повинні цитувати змінні. Це не коштує більше часу, ніж думати про те, чи потрібно це кожен раз. Але це дозволяє уникнути помилок.
Hauke ​​Laging

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

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

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: незв'язана змінна


І на вашу думку, це має сенс для перевірки часу виконання?
Hauke ​​Laging

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

@ n.st Я не згоден - null може бути настільки ж корисним значенням, як і не null, якщо ви плануєте це.
mikeserv

1
@ n.st Це не має сенсу для ОП, оскільки він не хоче захисту від доступу до невстановлених змінних (BTW: встановлена, але порожня змінна могла б викликати ту саму помилку, але не реагувати на ваш "захист"). Він хоче, щоб час запуску перевірив, чи не змінена / порожня змінна. Ваша пропозиція може бути корисною як загальна допомога розвитку, але не вирішує проблему ОП.
Hauke ​​Laging

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 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встановлюються в послідовності.
чепнер

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 .

Ця версія містить список змінних, щоб перевірити наявність, друкуючи ім'я відсутньої змінної та викликаючи помилку виклику до використання ().

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.