Как проверить, установлена ​​ли переменная в Bash?


1568

Как узнать, установлена ​​ли переменная в Bash?

Например, как я могу проверить, передал ли пользователь первый параметр функции?

function a {
    # if $1 is set ?
}

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

211
Примечание для тех, кто ищет решения: Есть много высоко оцененных ответов на этот вопрос, которые отвечают на вопрос «переменная непустая». Более корректирующие решения («набор переменных») упомянуты в ответах Дженса и Лайонела ниже.
Натан Кидд

8
Рассел Хармон и Симус также правы в своем -vтесте, хотя, по-видимому, он доступен только в новых версиях bashи не переносится между оболочками.
Грэм,

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

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

Ответы:


2305

(Обычно) правильный путь

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 "" ]и так)).

Однако, хотя кавычки могут быть безопасно опущены, и это было не сразу очевидно для всех (даже для первого автора этого объяснения кавычек, который также является основным кодировщиком Bash), иногда было бы лучше написать решение с кавычками как [ -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" ].
Грэм,

11
Это не работает Я получаю «not set» независимо от того, установлено ли значение var или нет (очищается с помощью «unset var», «echo $ var» не выводит).
Brent212

10
Для синтаксиса решения $ {параметр + слово} раздел официального руководства - gnu.org/software/bash/manual/… ; однако, ошибка в том, что он не очень четко упоминает этот синтаксис, а говорит просто кавычка (пропуск двоеточия [":"] приводит к проверке только для неустановленного параметра. .. $ {параметр: + слово} [означает] Если параметр равен нулю или не установлен, ничто не подставляется, в противном случае подставляется расширение слова.); цитируемая ссылка 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

910

Для проверки ненулевой / ненулевой строковой переменной, т.е. если она установлена, используйте

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
Вопрос заключается в том, как можно увидеть , если переменная установлена . Тогда вы не можете предположить , что переменная будет установлена.
Здравствуйте, до свидания,

7
Я согласен, []работает лучше, особенно для сценариев оболочки (не Bash).
Hengjie


63
Обратите внимание, что -nэто тест по умолчанию, так что проще [ "$1" ]или лучше [[ $1 ]]работать. Также обратите внимание, что с [[ ]] цитированием нет необходимости .
TNE

479

Вот как проверить, является ли параметр не установленным , пустым («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: Расширение параметров :

Во всех случаях, показанных с «заменой», выражение заменяется показанным значением. Во всех случаях, показанных с помощью «assign», параметру присваивается это значение, которое также заменяет выражение.

Чтобы показать это в действии:

+--------------------+----------------------+-----------------+-----------------+
|   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}echos "foo"; set --; echo ${1:-set but null or unset}отголоски установлены, но ноль ...
Jens

4
@HelloGoodbye Позиционные параметры могут быть установлены с помощью 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 nounsetmode, в отличие от многих других подходов, таких как использование [ -z.


9
В bash 4.1.2, независимо от того, установлена ​​ли переменная, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Дан

32
Аргумент '-v' для встроенной команды 'test' был добавлен в bash 4.2.
Ti Strga

19
аргумент -v был добавлен в качестве дополнения к опции оболочки -u (существительное). При включенном 'nounset' (set -u) оболочка вернет ошибку и завершит работу, если она не является интерактивной. $ # был хорош для проверки позиционных параметров. Тем не менее, именованные переменные нуждались в каком-то ином способе, кроме сгущения, для идентификации их как неустановленных. К сожалению, эта функция появилась слишком поздно, потому что многие обязаны быть совместимыми с более ранними версиями, и в этом случае они не могут ее использовать. Единственный другой вариант - использовать трюки или «хаки», чтобы обойти это, как показано выше, но это наиболее не элегантно.
osirisgothra

2
Обратите внимание, что это не работает для переменных, которые объявлены, но не установлены. напр.declare -a foo
miken32

17
Вот почему я продолжаю читать после принятого / получившего наибольшее количество голосов ответа.
Джо

176

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

if [ -z "$1" ]

Это успешно, если $ 1 ноль или не установлен


46
Существует разница между неустановленным параметром и параметром с нулевым значением.
chepner

21
Я просто хочу быть педантичным и указать, что это противоположный ответ на вопрос. Вопросы спрашивают, установлена ​​ли переменная, а не установлена ​​ли переменная.
Philluminati

47
[[ ]]это не что иное, как ловушка переносимости. if [ -n "$1" ]следует использовать здесь.
Дженс

73
Этот ответ неверен. Вопрос спрашивает, как определить, установлена ​​ли переменная, а не установлено ли для нее непустое значение. Данный foo="", $fooимеет значение, но -zпросто сообщит, что он пуст. И -z $fooвзорвется, если у вас есть set -o nounset.
Кит Томпсон,

4
Это зависит от определения «переменная установлена». Если вы хотите проверить, установлена ​​ли переменная в ненулевую строку, то ответ mbranning будет правильным. Но если вы хотите проверить, объявлена ​​ли переменная, но не инициализирована (то есть foo=), тогда ответ Рассела верен.
поток

77

Я всегда нахожу таблицу 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")
  • empty означает VARIABLEпусто / null ( VARIABLE="")
  • неустановленное средство VARIABLEне существует ( unset VARIABLE)

Ценности:

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

1
Вы исправили только те, которые являются реальными ошибками кодирования (случаи нежелательной косвенности при работе с переменными). Я сделал правку, чтобы исправить еще несколько случаев, когда доллар имеет значение при интерпретации описания. Кстати, все переменные оболочки (за исключением массивов) являются строковыми переменными; может случиться так, что они содержат строку, которую можно интерпретировать как число. Разговор о струнах только затуманивает сообщение. Кавычки не имеют ничего общего со строками, они просто альтернатива экранированию. VARIABLE=""может быть написано как VARIABLE=. Тем не менее, первый более читабелен.
Палек

Спасибо за таблицу, это очень полезно, однако я пытаюсь завершить работу скрипта, если он не установлен или пуст. Но код выхода - 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}чтобы получить, xесли $variableон установлен) почти полгода назад. Также в нем есть гораздо более подробное объяснение, почему это правильно.
Марк Хаферкамп

но ${variable+x}менее
читабелен

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задана пустая строка.
Здравствуйте, до свидания

2
Вторая часть ответа (подсчет параметров) - полезный обходной путь для первой части ответа, которая не работает, если $1для нее задана пустая строка.
Марк Берри

Это не удастся, если включен set -o nounset.
Флим

21

Запись

Я даю сильно сфокусированный на Bash ответ из-за 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-совместимый скрипт, то вы не можете использовать массивы. Без массивов [ -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но SO не позволил бы мне редактировать один символ must (должно быть> 6 символов - даже пытался добавить пробел) но это не сработало). Кроме того, возможно, стоит изменить функцию для переключения $1для образца с именем переменной (например OUTPUT_DIR), поскольку, как вы указали, специальные переменные, такие как здесь $1, не работают. В любом случае, это критическое редактирование, иначе код пытается создать файл с именем nullв относительном каталоге dev. Кстати - отличный ответ!
Джоанна

Кроме того, использование имени переменной без префикса $ также работает: declare -p PATH &> /dev/nullвозвращает 0, где, как declare -p ASDFASDGFASKDF &> /dev/nullвозвращает 1. (на выпуске bash 5.0.11 (1))
joehanna

1
Отличная работа! 1 слово предостережения: declare varобъявляет переменную var, но не устанавливает ее в терминах set -o nounset: Test withdeclare 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

Просто узнал что-то новое, спасибо! Пропуск двоеточия приводит к проверке только для неустановленного параметра. Другими словами, если двоеточие включено, оператор проверяет наличие как параметра, так и того, что его значение не равно нулю; если двоеточие опущено, оператор проверяет только наличие. Здесь нет разницы, но приятно знать.
Палек

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

Только проверка, нет выхода - пусто и неустановлено недействительно

Попробуйте эту опцию, если вы просто хотите проверить, установлено ли значение set = 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"

Только проверка, нет выхода - только пусто - НЕДОПУСТИМО

И это ответ на вопрос. Используйте это, если вы просто хотите проверить, установлено ли значение 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}.
Трипли

Откуда ты это взял? Чудесно! Это работает! И это коротко и элегантно! Спасибо!
Роджер

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 . Кроме того, они не являются динамическими, например, как проверить, определена ли переменная с именем «dummy»? Попробуй это:

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: интересная идея. Предлагаю вам опубликовать как отдельный ответ.
Кевинарпе

Использование «eval» делает это решение уязвимым для внедрения кода: $ is_var_defined 'x}]; echo "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задана пустая строка.
Здравствуйте, до свидания

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всегда установлено. Извините, это не удалось.

Я попробовал это, и это не сработало, может быть, моей версии bash не хватает поддержки для этого. ИДК, извините, если я ошибаюсь. :)

0

Я нашел (намного) лучший код для этого, если вы хотите что-то проверить $@.

если [[$ 1 = ""]]
тогда
  echo '$ 1 пусто'
еще
  echo '$ 1 заполнен'
фи

Почему это все? В $@Bash все существует, но по умолчанию оно пустое, поэтому test -zи test -nне может вам помочь.

Обновление: Вы также можете посчитать количество символов в параметрах.

если [$ {# 1} = 0]
тогда
  echo '$ 1 пусто'
еще
  echo '$ 1 заполнен'
фи

Я знаю, но это все. Если что-то пусто, вы получите ошибку! Сожалею. : P

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

11
Добро пожаловать в StackOverflow. Хотя ваш ответ приветствуется, лучше всего дать больше, чем просто код. Не могли бы вы добавить аргументацию к вашему ответу или небольшое объяснение? Смотрите эту страницу для других идей по расширению вашего ответа.
Celeo

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 вплоть до bash 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+ доступно косвенное расширение. Вы можете заменить long и complex (включая eval) test -n "$(eval "echo "\${${1}+x}"")"на эквивалент:[[ ${!1+x} ]]

0

Мне нравятся вспомогательные функции, чтобы скрыть грубые детали bash. В этом случае это добавляет еще больше (скрытой) грубости:

# 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 bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0

БОНУС: usecase

Я в основном использовать этот тест , чтобы дать (и обратно) параметры функцию в некоторой степени «элегантный» и безопасном способе (почти напоминающий интерфейс ...):

#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 -uдействуете, что напечатает unboud variableошибку
Майкл Хойбергер

0

Резюме

  • Используйте test -n "${var-}"для проверки, если переменная не пуста (и, следовательно, должна быть определена / установлена ​​тоже)
  • Используйте, test ! -z "${var+}"чтобы проверить, определена ли переменная / установлена ​​(даже если она пустая)

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

Ноты

  • Это решение должно работать во всех оболочках POSIX (sh, bash, zsh, ksh, dash)
  • Некоторые другие ответы на этот вопрос верны, но могут вводить в заблуждение людей, неопытных в создании сценариев оболочки, поэтому я хотел предоставить ответ TLDR, который будет наименее запутанным для таких людей.

объяснение

Чтобы понять , как это решение работает, вы должны понимать POSIX testкоманды и расширение параметров POSIX оболочки ( спецификация ), поэтому давайте рассмотрим самые основы , необходимых для понимания ответа.

Команда test оценивает выражение и возвращает истину или ложь (через состояние выхода). Оператор -nвозвращает true, если операнд является непустой строкой. Так, например, test -n "a"возвращает true, а test -n ""возвращает false. Теперь, чтобы проверить, не является ли переменная пустой (что означает, что она должна быть определена), вы можете использовать test -n "$var". Однако некоторые сценарии оболочки имеют опцию set ( set -u), которая заставляет любую ссылку на неопределенные переменные выдавать ошибку, поэтому, если переменная varне определена, выражение $aвызовет ошибку. Чтобы правильно обработать этот случай, вы должны использовать расширение переменной, которое скажет оболочке заменить переменную альтернативной строкой, если она не определена, избегая вышеупомянутой ошибки.

Расширение переменной ${var-}означает: если переменная varне определена (также называется «unset»), замените ее пустой строкой. Так test -n "${var-}"что вернет true, если $varне пусто, что почти всегда то, что вы хотите проверить в скриптах оболочки. Обратная проверка, если $varона не определена или не пуста, будет test -z "${var-}".

Теперь перейдем ко второму варианту использования: проверка, varопределена ли переменная , пустая или нет. Это менее распространенный вариант использования и немного более сложный, и я бы посоветовал вам прочитать отличный ответ Лайонела, чтобы лучше его понять.



-1

Если вы хотите проверить, является ли переменная связанной или несвязанной, это работает хорошо, даже после того, как вы включили опцию nounset:

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отредактированы. Это также меняет ваши настройки таким образом, что это неудобно, чтобы отменить.
Кит Томпсон,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.