Як перевірити, чи встановлена ​​змінна в Bash?


1566

Як дізнатись, чи вказана змінна в Bash?

Наприклад, як я можу перевірити, чи користувач дав перший параметр функції?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Єнс

210
Примітка для тих, хто шукає рішення: Є багато високо оцінених відповідей на це запитання, які відповідають на питання "мінлива, не порожня". Більше рішень для корекції ("є змінною множиною") згадуються у відповідях Йенсом та Ліонелем нижче.
Натан Кідд

8
Також Рассел Гармон і Сеамус виправдані у своєму -vтесті, хоча це, здавалося б, доступне лише в нових версіях bashі не переноситься через оболонки.
Graeme

5
Як вказував @NathanKidd, правильні рішення дають Ліонель та Єнс. prosseek, ви повинні переключити прийняту відповідь на одну з них.
Гаррет

3
... або неправильну відповідь можна спростувати більш вибагливими серед нас, оскільки @prosseek не вирішує проблему.
dan3

Відповіді:


2299

(Зазвичай) Правильний шлях

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

де ${var+x}є розширення параметра, яке не varвизначає значення, якщо воно не встановлено, і замінює рядок xінакше.

Цитата Відступ

Цитати можна опустити (тому ми можемо сказати ${var+x}замість цього "${var+x}"), оскільки цей синтаксис та використання гарантують, що це розшириться лише до чогось, що не потребує лапок (оскільки воно або розширюється на x(що не містить перерв слова, тому воно не потребує лапок), або нічого (що призводить до того [ -z ], що зручно оцінювати до того самого значення (true), що [ -z "" ]і)).

Однак, хоча цитати можуть бути безпечно опущені, і це було не відразу очевидно для всіх (навіть першому автору цього цитування не було очевидно, хто також є основним кодером Баша), іноді було б краще написати рішення з цитатами як [ -z "${var+x}" ], при дуже малій можливій вартості штрафу за швидкість O (1). Перший автор також додав це як коментар поруч із кодом, використовуючи це рішення, даючи URL-адресу цієї відповіді, яка тепер також включає пояснення, чому цитати можна безпечно опустити.

(Часто) Невірний шлях

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Це часто неправильно, оскільки він не розрізняє змінну, яка не встановлена, і змінну, встановлену в порожній рядок. Тобто, якщо var='', тоді вищезгадане рішення виведе "var is blank".

Відмінність між unset і "set to empty string" є важливим у ситуаціях, коли користувачеві необхідно вказати розширення або додатковий список властивостей, і не визначаючи їх за замовчуванням на не порожнє значення, тоді як вказувати порожній рядок повинен змусити скрипт використовувати порожнє розширення або список додаткових властивостей.

Однак різниця може бути не суттєвою у кожному сценарії. У тих випадках [ -z "$var" ]буде просто чудово.


29
@Garrett, ваша редакція зробила цю відповідь неправильною, ${var+x}це правильна заміна. Використання [ -z ${var:+x} ]не дає іншого результату, ніж [ -z "$var" ].
Graeme

11
Це не працює. Я отримую "не встановлено", незалежно від того, встановлено чи ні значення var (очищено з "unset var", "echo $ var" не дає результату).
Brent212

10
Для синтаксису рішення $ {параметр + слово}, офіційний розділ керівництва - gnu.org/software/bash/manual/… ; однак, помилка в цьому, він не дуже чітко згадує цей синтаксис, але говорить просто цитата (Опускаючи двокрапку [":"] приводить до тесту лише для параметра, який не встановлений ... $ {parameter: + word} [означає] Якщо параметр є нульовим або не встановленим, нічого не замінено, інакше розширення слова замінено.); цитований ref pubs.opengroup.org/onlinepubs/9699919799/utilities/… (добре знати) тут набагато чіткіші документи.
Архитектор долі

7
Схоже, лапки потрібні при використанні декількох виразів, наприклад, [ -z "" -a -z ${var+x} ]отримує bash: [: argument expectedв bash 4.3-ubuntu (але не, наприклад, zsh). Мені дуже не подобається відповідь, використовуючи синтаксис, який у деяких випадках працює.
FauxFaux

41
Використання простого [ -z $var ]- це не більше "неправильно", ніж опускання цитат. У будь-якому випадку ви робите припущення щодо свого вкладу. Якщо ви добре ставитеся до порожнього рядка як до невстановленого, [ -z $var ]це все, що вам потрібно.
nshew

909

Щоб перевірити наявність ненульової / ненульової рядкової змінної, тобто якщо вона встановлена, використовуйте

if [ -n "$1" ]

Це навпаки -z. Я вважаю, що використовую -nбільше-z .

Ви б використовували його так:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Я, як правило, віддаю перевагу [[ ]]над тим [ ], що [[ ]]є більш потужним і викликає менше проблем у певних ситуаціях (див. Це питання для пояснення різниці між ними). Питання конкретно запитує рішення bash і не згадує жодних вимог щодо портативності.
Потік

18
Питання полягає в тому, як можна побачити , якщо змінна встановлена . Тоді ви не можете припустити , що змінна буде встановлена.
HelloGoodbye

7
Я погоджуюся, []працює краще, особливо для оболонок (не-bash) сценаріїв.
Хенджі


63
Зауважте, що -nце тест за замовчуванням, тому більш просто [ "$1" ]або [[ $1 ]]працюйте також. Також зверніть увагу, що з [[ ]] цитуванням це зайве .
TNE

478

Ось як перевірити, чи параметр не встановлений , чи порожній ("Null") чи встановлений зі значенням :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Джерело: POSIX: Розширення параметра :

У всіх випадках, показаних із "замінником", вираз замінюється показаним значенням. У всіх випадках, показаних з "призначити", параметру присвоюється це значення, яке також замінює вираз.

Щоб показати це в дії:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@HelloGoodbye Так, це спрацьовує: set foo; echo ${1:-set but null or unset}лунає "foo"; set --; echo ${1:-set but null or unset}відлуння встановлені, але нульові ...
Єнс

4
@HelloGoodbye Позиційні параметри можна встановити за допомогою, uh, set:-)
Jens

95
Ця відповідь дуже бентежить. Будь-які практичні приклади, як використовувати цю таблицю?
Бен Девіс

12
@BenDavis Деталі пояснюються за посиланням на стандарт POSIX, в якому посилається на розділ, з якого взята таблиця. Одна конструкція, яку я використовую майже в будь-якому сценарії, який я пишу, - : ${FOOBAR:=/etc/foobar.conf}це встановити значення змінної за замовчуванням для змінної, якщо вона не встановлена ​​або нульова.
Єнс

1
parameter- будь-яке ім'я змінної. wordце рядок, який слід замінити, залежно від того, який синтаксис у таблиці ви використовуєте, і чи вказана змінна $parameter, встановлена, але нульова, або не встановлена. Не робити речі складнішими, але wordтакож можуть містити змінні! Це може бути дуже корисно для передачі необов'язкових аргументів, розділених символом. Наприклад: ${parameter:+,${parameter}}виводиться розділена кома, ,$parameterякщо вона встановлена, але не є нульовою. У цьому випадку wordце,${parameter}
TrinitronX

260

Хоча більшість наведених тут методів є правильними, bash 4.2 підтримує фактичний тест на наявність змінної ( man bash ), а не тестування значення змінної.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Зокрема, такий підхід не спричинить помилку при використанні для перевірки невстановленої змінної в set -u/ set -o nounsetрежимі, на відміну від багатьох інших підходів, таких як використання [ -z.


9
У bash 4.1.2, незалежно від того, чи встановлена ​​змінна, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
Аргумент '-v' до 'test' вбудованого додано в базі 4.2.
Ti Strga

19
Аргумент -v був доданий як доповнення до параметра оболонки -u (іменник). Якщо увімкнено функцію 'іменник' (set -u), оболонка поверне помилку та припиниться, якщо вона не є інтерактивною. $ # була гарна для перевірки позиційних параметрів. Однак названим змінним потрібен був інший спосіб, крім клобірування, для ідентифікації їх як невстановлених. Прикро, що ця функція прийшла так пізно, оскільки багато хто з них повинен бути сумісний з більш ранньою версією, і в такому випадку вони не можуть її використовувати. Єдиний інший варіант - використовувати хитрощі або "хакі", щоб обійти її, як показано вище, але це неелегантно.
osirisgothra

2
Зауважте, це не працює для змінних, які оголошуються, але не встановлюються. Наприкладdeclare -a foo
miken32

17
Ось чому я продовжую читати після прийнятої / найбільш голосової відповіді.
Джо

176

Існує багато способів зробити це одним із них:

if [ -z "$1" ]

Це вдається, якщо $ 1 недійсне або не встановлено


46
Існує різниця між невстановленим параметром і параметром з нульовим значенням.
чепнер

21
Я просто хочу бути педантичним і зазначити, що це протилежна відповідь на те, що постає питання. Питання задають, чи встановлена ​​змінна IS, ні, якщо змінна не встановлена.
Philluminati

47
[[ ]]це не що інше, як підводний рух. if [ -n "$1" ]слід використовувати тут.
Єнс

73
Ця відповідь невірна. Питання задає спосіб сказати, чи встановлена ​​змінна, а не встановлена ​​не порожня величина. Дано foo="", $fooмає значення, але -zбуде лише повідомляти, що воно порожнє. І -z $fooпідірветься, якщо у вас є set -o nounset.
Кіт Томпсон

4
Це залежить від визначення "встановлена ​​змінна". Якщо ви хочете перевірити, чи змінна встановлена ​​на нульовий рядок, то відповідь mbranning правильна. Але якщо ви хочете перевірити, чи змінна оголошена, але не ініціалізована (тобто foo=), то відповідь Русселя правильна.
Потік

76

Я завжди знаходжу таблицю POSIX в іншій відповіді повільно громіти, тому ось я прийму це:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Зауважте, що у кожної групи (з попередньою двокрапкою та без неї) є однакові випадки безлічі та неустановлених знаків , тому єдине, що відрізняється, - це те, як порожня справ.

З попередньою двокрапкою порожні та незареєстровані випадки однакові, тому я б використовував ті, де це можливо (тобто використовувати :=, а не просто =, тому що порожній регістр є несуперечливим).

Рубрики:

  • встановлений засіб VARIABLEне порожній ( VARIABLE="something")
  • порожній засіб VARIABLEпорожній / null ( VARIABLE="")
  • unset засобів VARIABLEне існує ( unset VARIABLE)

Значення:

  • $VARIABLE означає, що результат є вихідним значенням змінної.
  • "default" означає, що результатом була надана рядок заміни.
  • "" означає, що результат є null (порожній рядок).
  • exit 127 означає, що сценарій припиняє виконувати код виходу 127.
  • $(VARIABLE="default")означає, що результат є вихідним значенням змінної, а надана рядок заміни присвоюється змінній для подальшого використання.

1
Ви виправили лише ті, що є фактичними помилками кодування (випадки небажаної непрямості під час роботи зі змінними). Я вніс зміни, щоб виправити ще кілька випадків, коли долар змінює інтерпретацію опису. BTW, всі змінні оболонки (за винятком масивів) є рядковими змінними; може просто статися, що вони містять рядок, який можна інтерпретувати як число. Якщо говорити про рядки, лише запотіває повідомлення. Котирування не мають нічого спільного з рядками, вони просто альтернатива втечі. VARIABLE=""можна записати як VARIABLE=. І все-таки колишнє читабельніше.
Palec

Дякую за таблицю, це дуже корисно, проте я намагаюся вийти зі сценарію, якщо він не встановлений чи порожній. Але вихідний код, який я отримую, - 1, а не 127.
Астронавт

64

Щоб побачити, чи є змінна непорожня, я використовую

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

Протилежні тести, якщо змінна або знята, або порожня:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Щоб побачити, чи встановлена ​​змінна (порожня чи непорожня), я використовую

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Протилежні тести, якщо змінна не встановлена:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Я теж деякий час використовував це; просто скороченим способом:[ $isMac ] && param="--enable-shared"

@Bhaskar Я думаю, це тому, що ця відповідь вже дала по суті таку ж відповідь (використання ${variable+x}для отримання xiff $variableвстановлено) майже на півроку раніше. Крім того, він має набагато більше деталей, що пояснюють, чому це правильно.
Марк Хаферкамп

але ${variable+x}менш читабельна (& очевидно)
knocte

35

У сучасній версії Bash (4.2 або новішої версії, я думаю, я не знаю точно), я б спробував це:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
Також зауважте, що [ -v "$VARNAME" ]це неправильно, але просто виконує інший тест. Припустимо VARNAME=foo; потім він перевіряє, чи існує змінна назва, fooяка встановлена.
чепнер

5
Примітка для тих, хто бажає переносимості, -vне відповідає POSIX
kzh

Якщо хтось отримує [: -v: unexpected operator, треба використовувати, bashа не використовувати sh.
коппор

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Хоча для аргументів зазвичай найкраще перевірити $ #, яка є кількістю аргументів, на мою думку.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
Перший тест - відсталий; Я думаю, ти хочеш [ "$1" != "" ](або [ -n "$1" ]) ...
Гордон Девіссон

10
Це не вдасться, якщо $1встановлено порожній рядок.
HelloGoodbye

2
Друга частина відповіді (параметри підрахунку) є корисним вирішенням для першої частини відповіді, яка не працює, коли $1встановлено порожній рядок.
Марк Беррі

Це не вдасться, якщо set -o nounsetвін увімкнено.
Flimm

21

Примітка

Я даю сильно зосереджену відповідь Баша через bashтег.

Коротка відповідь

Поки ви маєте справу лише з названими змінними в Bash, ця функція завжди повинна говорити вам, чи була змінена установлена, навіть якщо це порожній масив.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Чому це працює

У Bash (щонайменше, як 3.0), якщо varце оголошена / встановлена ​​змінна, declare -p varвиводиться declareкоманда, яка встановила б змінну varв залежності від її поточного типу та значення та повертає код стану 0(успіх). Якщо varце не визначено, declare -p varвиводить повідомлення про помилку stderrта повертає код статусу 1. Використовуючи &>/dev/null, переадресовує як звичайний, так stdoutі stderrвихідний на/dev/null ніколи не було видно, і без зміни коду статусу. Таким чином, функція повертає лише код статусу.

Чому інші методи (іноді) провалюються в Bash

  • [ -n "$var" ]: Це перевіряє, чи ${var[0]}немає порожнього. (У Bash $var- те саме, що і ${var[0]}.)
  • [ -n "${var+x}" ]: Це перевіряє лише, якщо ${var[0]}встановлено.
  • [ "${#var[@]}" != 0 ]: Це перевіряє, чи встановлено принаймні один індекс $var.

Коли цей метод не працює в Bash

Це працює тільки для іменованих змінних ( в тому числі $_), а НЕ деяких спеціальних змінних ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., і будь-який я забув). Оскільки жоден з них не є масивами, стиль POSIX [ -n "${var+x}" ]працює для всіх цих спеціальних змінних. Але будьте обережні, щоб перетворити його у функцію, оскільки багато спеціальних змінних змінюють значення / існування, коли викликаються функції.

Примітка про сумісність оболонки

Якщо ваш сценарій має масиви, і ви намагаєтеся зробити його сумісним із якомога більшою кількістю оболонок, тоді typeset -pзамість цього використовуйте їх declare -p. Я читав, що ksh підтримує лише колишній, але не зміг перевірити це. Я знаю, що Bash 3.0+ і Zsh 5.5.1 кожен підтримують і те, typeset -pі declare -pрізниться лише тим, що одне є альтернативою іншому. Але я не перевіряв відмінності за межами цих двох ключових слів, і я не перевіряв інші оболонки.

Якщо вам потрібен, щоб ваш сценарій сумісний з POSIX sh, ви не можете використовувати масиви. Без масивів [ -n "{$var+x}" ]працює.

Код порівняння для різних методів у Bash

Ця функція знімає змінну var, eval- пройдений код, проводить тести, щоб визначити, чи varвстановлений evalкод d, і, нарешті, показує отримані коди статусу для різних тестів.

Я пропускаю test -v var, [ -v var ]і [[ -v var ]]тому, що вони дають однакові результати, ніж стандарт POSIX [ -n "${var+x}" ], вимагаючи Bash 4.2+. Я також пропускаю, typeset -pтому що це те саме, що і declare -pв оболонках, які я протестував (Bash 3.0 через 5.0 і Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Код тестового випадку

Зауважте, що результати тестування можуть бути несподіваними через те, що Bash трактує нечислові індекси масиву як "0", якщо змінна не була оголошена асоціативним масивом. Також асоціативні масиви дійсні лише в Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Тестовий вихід

Випробовувані Мнемоніки в заголовку рядки відповідають [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], і declare -p var, відповідно.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Підсумок

  • declare -p var &>/dev/null є (100%?) надійним для тестування названих змінних в Bash, щонайменше, від 3.0.
  • [ -n "${var+x}" ] надійний у ситуаціях, сумісних з POSIX, але не може обробляти масиви.
  • Інші тести існують для перевірки, чи змінна непорожня, і для перевірки оголошених змінних в інших оболонках. Але ці тести не підходять ні для сценаріїв Bash, ні для POSIX.

3
З відповідей, які я спробував, ця ( declare -p var &>/dev/null) - ТІЛЬКИ та, яка працює. ДЯКУЮ ТОБІ!
user1387866

2
@ mark-haferkamp Я спробував відредагувати вашу відповідь, щоб додати критичний попередній "/" до вашого коду функції, щоб він читався, ... &> /dev/nullале ТАК не дозволив би мені змінити один символ 🙄 (повинен бути> 6 символів - навіть намагався додати пробіл) але це не спрацювало) Крім того, можливо, варто змінити функцію вимкнення $1для вибірки, названої змінною (наприклад OUTPUT_DIR), оскільки, як ви вказали, спеціальні змінні на зразок $1тут не працюють. У будь-якому випадку, це критичне редагування, інакше код шукає створити файл, викликаний nullу відповідному каталозі, який називаєтьсяdev . Дорога - чудова відповідь!
Джоеханна

Також використання імені змінної без префікса $ також працює: declare -p PATH &> /dev/nullповертає 0 де як declare -p ASDFASDGFASKDF &> /dev/nullповернення 1. (на bash 5.0.11 (1)
-release

1
Чудова робота! 1 слово застереження: declare varоголошує var змінною, але не встановлює її в термінах set -o nounset: Тест зdeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna Дякуємо за те, що злопали /! Я це виправив, але вважаю, що логіка досить заплутана, щоб гарантувати функцію, особливо з огляду на коментар @ xebeche. @xebeche Це незручно для моєї відповіді ... Схоже, мені доведеться спробувати щось на кшталтdeclare -p "$1" | grep = або test -v "$1".
Марк Хаферкамп

19

Для тих, хто шукає, щоб перевірити, чи не встановлено чи порожньо в сценарії з set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

Черговий [ -z "$var" ] перевірка не завершиться функцією var; unbound variableif, set -uале [ -z "${var-}" ] розшириться на порожню рядок, якщо varвона встановлена ​​без встановлення.


Вам не вистачає товстої кишки. Це має бути ${var:-}, ні ${var-}. Але в Bash, я можу підтвердити, що це працює навіть без товстої кишки.
Палець

3
${var:-}і ${var-}означають різні речі. ${var:-}буде розгорнуто до порожнього рядка, якщо він не varвстановлений або недійсний, і ${var-}розгорнеться до порожнього рядка, лише якщо він не встановлений.
RubenLaguna

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

17

Ви хочете вийти, якщо він не встановлений

Це працювало для мене. Я хотів, щоб мій сценарій вийшов із повідомленням про помилку, якщо параметр не був встановлений.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Це повертається з помилкою під час його запуску

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Поставте лише прапорець, жодного виходу немає - Порожні та Скасовані недійсні

Спробуйте цей варіант, якщо ви просто хочете перевірити, чи встановлено значення = VALID або unset / empty = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Або, навіть короткі тести ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Позначте лише, немає виходу - INVALID - лише порожній

І це відповідь на запитання. Використовуйте це, якщо ви просто хочете перевірити, чи значення set / empty = VALID або unset = INVALID.

ПРИМІТКА, значення "1" в "..- 1}" є незначним, воно може бути будь-яким (наприклад x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Короткі тести

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Цю відповідь я присвячую @ mklement0 (коментарі), який кинув мені виклик, щоб точно відповісти на питання.

Довідка http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Щоб перевірити, чи встановлена ​​змінна з порожнім значенням, використовуйте [ -n "$x" ], як уже вказали інші.

Здебільшого це гарна ідея обробляти змінну, яка має порожнє значення так само, як і змінну, яка не встановлена. Але ви можете розрізнити два, якщо вам потрібно: [ -n "${x+set}" ]( "${x+set}"розширюється на, setякщо xвстановлено, і на порожню рядок, якщо xвона не встановлена).

Щоб перевірити, чи був переданий параметр, протестуйте $#, яка кількість параметрів, переданих функції (або сценарію, коли він не знаходиться у функції) (див . Відповідь Павла ).


14

Прочитайте розділ «Розширення параметрів» у розділі bash сторінці чоловіка. Розширення параметрів не дає загального тесту для встановленої змінної, але є кілька речей, які ви можете зробити для параметра, якщо він не встановлений.

Наприклад:

function a {
    first_arg=${1-foo}
    # rest of the function
}

буде встановлено first_argрівним, $1якщо він призначений, інакше він використовує значення "foo". Якщо aабсолютно необхідно взяти один параметр, і немає хорошого за замовчуванням, ви можете вийти із повідомленням про помилку, коли не вказано жодного параметра:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

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


1
Мене бентежить, що жоден з інших відповідей не згадує простий і елегантний : ${var?message}.
tripleee

Звідки ти це взяв? Чудово! Це джуста працює! І це коротко і елегантно! Дякую!
Роджер

13

У bash ви можете використовувати -vвсередині [[ ]]вбудованого:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

Використання [[ -z "$var" ]]- це найпростіший спосіб дізнатися, чи була встановлена ​​змінна чи ні, але цей параметр -zне розрізняє неустановлену змінну та встановлену змінну у порожній рядок:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Найкраще перевірити це відповідно до типу змінної: змінної env, параметра або звичайної змінної.

Для змінної env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Для параметра (наприклад, для перевірки наявності параметра $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Для звичайної змінної (використовуючи допоміжну функцію, щоб зробити це елегантно):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Примітки:

  • $#: дає кількість позиційних параметрів.
  • declare -p: дає визначення змінної, переданої як параметр. Якщо він існує, повертає 0, якщо ні, повертає 1 і друкує повідомлення про помилку.
  • &> /dev/null: пригнічує вихід з, declare -pне впливаючи на його код повернення.

5

Наведені вище відповіді не працюють, коли set -uвключена опція Bash . Крім того, вони не динамічні, наприклад, як тестувати змінну з назвою "манекен" визначено? Спробуйте це:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Пов'язане: У Bash, як перевірити, чи визначена змінна в режимі "-u"


1
Мені хотілося б так багато знати, чому цю відповідь було оскаржено ... Це чудово працює для мене.
LavaScornedOven

У Bash, ви могли б зробити те ж саме без Eval: зміни eval "[ ! -z \${$1:-} ]"в непряму оцінку: [ ! -z ${!1:-} ];.

@BinaryZebra: Цікава ідея. Я пропоную вам опублікувати як окрему відповідь.
kevinarpe

Використання "eval" робить це рішення вразливим до введення коду: $ is_var_defined 'x}]; відлуння "gotcha"> & 2; # '
gotcha

4

Ви можете зробити:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}

9
Або ви могли б зробити -nі не заперечувати це -z.
Призупинено до подальшого повідомлення.

1
вам потрібні цитати навколо $1, тобто [ ! -z "$1" ]. Або ви можете написати [[ ! -z $1 ]]в bash / ksh / zsh.
Жил "ТАК - перестань бути злим"

5
Це не вдасться, якщо $1встановлено порожній рядок.
HelloGoodbye

4

Мій кращий спосіб такий:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Отже, якщо встановлена ​​змінна, вона стає "запереченням отриманого false" (що буде true= "встановлено").

І, якщо він не встановлений, він стане "запереченням отриманого true" (як оцінюється порожній результат true) (так закінчиться як false"= НЕ встановлено").



1

У оболонці ви можете використовувати -z оператором, який є True, якщо довжина рядка дорівнює нулю.

Простий однолінійний параметр для встановлення за замовчуванням, MY_VARякщо він не встановлений, інакше необов'язково ви можете відобразити повідомлення:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2і до $Nцього завжди встановлено. Вибачте, це не вдалося.

Я спробував це, і це не вийшло, можливо, у моїй баш-версії немає підтримки для цього. Idk, вибач, якщо я помиляюся. :)

0

Я знайшов (набагато) кращий код, щоб це зробити, якщо ви хочете перевірити що-небудь в $@.

якщо [[$ 1 = ""]]
тоді
  відлуння "$ 1 порожнє"
ще
  відлуння "$ 1 заповнюється"
фі

Чому це все? Все в $@існує в Bash, але за замовчуванням це порожній, так test -zі test -nне змогли допомогти вам.

Оновлення: Ви також можете порахувати кількість символів у параметрах.

якщо [$ {# 1} = 0]
тоді
  відлуння "$ 1 порожнє"
ще
  відлуння "$ 1 заповнюється"
фі

Я знаю, але це все. Якщо щось порожнє, ви отримаєте помилку! Вибачте. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
Ласкаво просимо до StackOverflow. Хоча ваша відповідь оцінена, краще дати більше, ніж просто код. Ви можете додати міркування до своєї відповіді чи невелике пояснення? Дивіться цю сторінку для інших ідей щодо розширення вашої відповіді.
Селео

0

Це те, що я використовую щодня:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Це добре працює в Linux та Solaris, аж до версії 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Оскільки Bash 2.0+ є непрямим розширенням. Ви можете замінити довгий і складний (з включенням також eval) test -n "$(eval "echo "\${${1}+x}"")"на еквівалент:[[ ${!1+x} ]]

0

Мені подобаються допоміжні функції, щоб приховати грубі деталі баш. У цьому випадку це додає ще більше (прихованої) грубості:

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Оскільки у мене вперше виникли помилки в цій реалізації (надихнувшись відповідями Йенса та Ліонеля), я придумав інше рішення:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Я вважаю, що це більш прямолінійно, більш боязко і легше зрозуміти / запам'ятати. Тестовий випадок показує, що він еквівалентний:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

Тестовий приклад показує також , що local varробить НЕ оголошувати вар (якщо не слід «=»). Довгий час я думав, що я оголосив змінні таким чином, просто щоб виявити, що я просто висловив свій намір ... Я думаю, що це неоперація.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky foor: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0

БОНУС: користувальниця

Я в основному використовую цей тест для надання (і повернення) параметрів функцій дещо "елегантним" і безпечним способом (майже схожим на інтерфейс ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Якщо викликається з усіма необхідними оголошеними змінними:

Привіт Світ!

ще:

Помилка: змінна не оголошена: outputStr


0

Провівши всі відповіді, це також працює:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

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


не буде працювати, коли ви set -uunboud variable
дієте,

-1

Щоб перевірити, встановлена ​​чи ні вар

var=""; [[ $var ]] && echo "set" || echo "not set"

3
Це не вдасться, якщо $varвстановлено порожній рядок.
HelloGoodbye

-1

Якщо ви хочете перевірити, що змінна є зв'язаною чи незв'язаною, це спрацює навіть після ввімкнення параметра іменника:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Я думаю, ти маєш на увазі set -o nounset, ні set -o noun set. Це працює лише для змінних, які були exportвідредаговані. Він також змінює ваші налаштування таким чином, який незручно скасувати.
Кіт Томпсон

-1

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

if [ "$variable" = "" ]
    then
    echo "Variable X is empty"
fi

І, якщо хочете перевірити, чи не порожньо;

if [ ! "$variable" = "" ]
    then
    echo "Variable X is not empty"
fi

Це воно.


4
Питання задає питання, як перевірити, чи встановлена ​​змінна, а не, якщо змінна порожня.
HelloGoodbye

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