Перевірте, чи встановлено змінну в bash, використовуючи “set -o nounset”


95

Наступний код виходить із помилкою незв’язаної змінної. Як це виправити, використовуючи параметр set -oіменника?

#!/bin/bash

set -o nounset

if [ ! -z ${WHATEVER} ];
 then echo "yo"
fi

echo "whatever"

Відповіді:


98
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

if [ ! -z ${VALUE} ];
 then echo "yo"
fi

echo "whatever"

У цьому випадку VALUEзакінчується порожнім рядком, якщо WHATEVERне встановлено. Ми використовуємо {parameter:-word}розширення, яке ви можете знайти в man bashрозділі "Розширення параметрів".


14
просто замініть, якщо [! -z $ {VALUE}]; з if [! -z $ {ЩОБ: -}];
Ангелом

18
:-перевіряє, чи змінна не встановлена чи порожня. Якщо ви хочете перевірити тільки це знято з охороною, використовуйте -: VALUE=${WHATEVER-}. Крім того, більш читабельний спосіб перевірити, чи порожня змінна:if [ "${WHATEVER+defined}" = defined ]; then echo defined; else echo undefined; fi
l0b0

1
Крім того, це не спрацює, якщо $WHATEVERмістить лише пробіли - див. Мою відповідь.
l0b0

10
Чи є причина використовувати " ! -z" замість просто " -n"?
Джонатан Хартлі

31

Вам потрібно процитувати змінні, якщо ви хочете отримати результат, який очікуєте:

check() {
    if [ -n "${WHATEVER-}" ]
    then
        echo 'not empty'
    elif [ "${WHATEVER+defined}" = defined ]
    then
        echo 'empty but defined'
    else
        echo 'unset'
    fi
}

Тест:

$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER='   '
$ check
not empty

Я спробував це , і я здивований це працює ... Все правильно , за винятком в відповідно "info bash", "${WHATEVER-}"повинні мати ":"(двокрапка) перед "-"(тире) , як: "${WHATEVER:-}"і "${WHATEVER+defined}"повинні мати двокрапка перед тим , "+"(плюс) , як: "${WHATEVER:+defined}". Для мене це працює в будь-якому випадку, з товстою кишкою або без неї. У деяких версіях 'nix це, ймовірно, не спрацює без включення двокрапки, тому його, мабуть, слід додати.
Kevin Fegan

8
Неа, -, +, :+, і :-все підтримуються. Колишній виявити , чи є змінна встановлена , а останній виявляє він встановлений або порожній . From man bash: "Опускання двокрапки призводить до тесту лише для параметра, який не встановлено."
l0b0

1
Неважливо =). Ви праві. Не знаю, як я це пропустив.
Кевін Феган

З документації : Іншим чином, якщо двокрапка включена, оператор перевіряє наявність обох параметрів та те, що його значення не є нульовим; якщо товста кишка опущена, оператор перевіряє лише наявність.
Проникність

8

Як щодо oneliner?

[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"

-z перевіряє як порожню, так і невстановлену змінну


2
Ні, -zперевіряє лише, якщо наступний параметр порожній. -zє лише аргументом [команди. Змінне розширення відбувається до того, як [ -zможна щось зробити.
долмен

1
Це здається правильним рішенням, оскільки воно не генерує помилку, якщо $ VAR не встановлено. @dolmen можете навести приклад, коли це не спрацювало?
Кріс Стричинський

@dolmen, прочитавши різні bash-ресурси про розширення параметрів і знайшовши надто складні відповіді, я не бачу в цьому нічого поганого. тому ваше "роз'яснення", хоча і технічно правильне, на практиці здається досить безглуздим, якщо тільки вам не потрібно диференціювати unset та empty. Я протестував не встановлені, порожні та не порожні, (bash 4), і він в основному робив те, що рекламується кожного разу.
JL

8

Припущення:

$ echo $SHELL
/bin/bash
$ /bin/bash --version | head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ set -o nounset

Якщо ви хочете, щоб неінтерактивний сценарій надрукував помилку та вийшов, якщо змінна має значення null або не встановлено:

$ [[ "${HOME:?}" ]]

$ [[ "${IAMUNBOUND:?}" ]]
bash: IAMUNBOUND: parameter null or not set

$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]
bash: IAMNULL: parameter null or not set

Якщо ви не хочете, щоб скрипт вийшов:

$ [[ "${HOME:-}" ]] || echo "Parameter null or not set."

$ [[ "${IAMUNBOUND:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

$ IAMNULL=""
$ [[ "${IAMUNNULL:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

Ви навіть можете використовувати [і ]замість [[і ]]вище, але останнє є кращим у Bash.

Зверніть увагу на те, що робить товста кишка вище. З документів :

Іншими словами, якщо двокрапка включена, оператор перевіряє наявність обох параметрів і те, що його значення не є нульовим; якщо товста кишка опущена, оператор перевіряє лише наявність.

Очевидно, немає потреби в -nабо -z.

Підсумовуючи, я, як правило, можу просто використовувати [[ "${VAR:?}" ]]. Згідно з прикладами, це друкує помилку і виходить, якщо змінна є нульовою або не встановлена.


4

Можна використовувати

if [[ ${WHATEVER:+$WHATEVER} ]]; then

але

if [[ "${WHATEVER:+isset}" == "isset" ]]; then

може бути більш читабельним.


Для порівняння рядків слід використовувати стандартний =оператор (POSIX) , а не ==для перенесення, а [замість цього, [[якщо це можливо.
Йенс,

2
@Jens Питання специфічне для bash і включає те, set -o nounsetщо характерне для bash. Якщо ви ставите a #!/bin/bashу верхній частині вашого сценарію, то насправді найкраще використовувати вдосконалення bash.
Бруно Броноскі

0

Хоча це не зовсім той випадок використання, про який просили вище, я виявив, що якщо ви хочете використовувати nounset(або-u ) поведінка за замовчуванням - це та, яку ви хочете: вийти з ненульового значення з описовим повідомленням.

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

Якщо все, що ви хочете - це повторити щось інше під час виходу або провести чистку, ви можете скористатися пасткою.

:-Оператор, ймовірно , що ви хочете , в іншому випадку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.