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

70

Мне нужно проверить существование переменной в ifзаявлении. Что-то с эффектом:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

И самый близкий вопрос к этому был этим , который фактически не отвечает на мой вопрос.

Интересно...
источник
Если вы хотите установить $somevarна значение / строки , если переменная не существует: ${somevar:=42}.
Сайрус
Лично я склонен проверять только на пустоту ( [ -n "$var" ]или [ ! -z "$var" ]). Я думаю, что проверки существования / небытия слишком тонки, и я предпочитаю мой код грубый и простой.
PSkocik
Зачем тебе это против [ -n "$var" ]? Связанный: stackoverflow.com/questions/3601515/…
Ciro Santilli 事件 改造 中心 法轮功 六四 事件

Ответы:

98

В современном bash (версия 4.2 и выше):

[[ -v name_of_var ]]

От help test:

-v VAR, True, если установлена ​​переменная оболочки VAR

Крис Даун
источник
3
Также работает с одиночными скобками: [ -v name_of_var ].
meuh
7
имейте в виду, что для хешей и массивов он возвращает false, если переменная не имеет элемента key / indice "0". Для namerefs он проверяет, определена ли цель. Это не работает для специальных параметров , как $1, $-, $#...
Stéphane Chazelas
4
Эта функция есть только в встроенной функции bash testили [; Это не доступно в /usr/bin/test. Сравните man testс help test.
Марк Лаката
@MarkLakata Правильно, потому что внешние команды не могут знать внутреннее состояние оболочки.
Крис Даун
ммм, не должно ли это быть всегда [[-v "$ name_of_var"]]?
Александр Миллс
24

Зависит от того, что вы подразумеваете под существующим .

Есть ли переменная , которая была объявлена , но не приписанные существуют ?

Есть ли массив (или хеш) переменной , которая была назначена пустой список существует ?

Есть ли переменная nameref ссылающийся на переменную , которая в настоящее время не назначается существует ?

Считаете ли вы $-, $#, $1переменные? (POSIX нет).

В борновоподобных оболочках канонический путь таков:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Это работает для скалярных переменных и других параметров , чтобы сказать , если переменная было присвоено значение (пустой или нет, автоматически, из среды, assigments, read, forили другие).

Для оболочек, которые имеют команду typesetили declare, они не будут сообщать как установленные переменные, которые были объявлены, но не назначены, кроме как в zsh.

Для оболочек , которые поддерживают массивы, за исключением , yashи zshчто не будет сообщать , как набор переменного массива , если элемент Indice-не был установлен.

Для bash(но не , ksh93ни zsh), для переменных типа ассоциативного массива , что бы не сообщать о них , как набор , если их элемент ключ «0» было установлено.

Для ksh93и bashдля переменных типа nameref это возвращает true, только если переменная, на которую ссылается nameref, сама считается установленной .

Для ksh, zshи bash, потенциально лучший подход может быть:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Для ksh93, zshи bash4,4 или выше, есть также:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Который будет сообщать о переменных, которые были установлены или объявлены.

Стефан Шазелас
источник
1
declare -p/ typeset -pработает bashсейчас тоже.
саз
1
@ CAS, нет. Не для объявленных, но не установленных переменных. Попробуйте bash -c 'typeset -i a; typeset -p a'сравнить с ksh93или zsh.
Стефан Шазелас
+1 Спасибо за указание меня здесь. Также см. Мой вопрос unix.stackexchange.com/q/280893/674
Тим
@cas, это изменилось в bash-4.4 (выпущен в сентябре 2016 года), я редактировал это в.
Стефан
9

Как уже упоминалось в ответе на SO , вот способ проверить:

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

где $ {somevar + x} - расширение параметра, которое оценивается как ноль, если переменная не установлена, и заменяет строку «x» в противном случае.

Использование -n, как предлагает другой ответ, только проверит, содержит ли переменная пустую строку. Это не проверит его существование.

shivams
источник
2
Вы должны процитировать, $somevarчтобы справиться IFS=x. Либо это, либо цитата x.
mikeserv
1
@mikeserv Спасибо, мне нравится узнавать о крайних случаях :) Вы имеете в виду if [ -z "${somevar+x}" ]? Будет ли цитирование по-прежнему необходимо внутри [[и ]]?
Том Хейл,
@ TomHale - да, в редких случаях. то [ testпроцедуры принимают параметры командной строки, так что обычные расширения и интерпретации как заказано обычным способом следует полагаться оказать при вызове теста применяется то , что вы должны вызвать для чтения , таким образом , любой командной строке программы. test {! + "!"}
mikeserv
@mikeserv Полагаю, вы ответили "да" на мой второй вопрос ... Первый тоже да?
Том Хейл,
1
+1; это кажется самым простым способом сделать это, когда set -uон действует, и версия Bash до 4.2.
Кайл Стрэнд
4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

или вы можете позволить вашей оболочке показать сообщение для вас:

(: "${somevar?}")
zsh: somevar: parameter not set
cuonglm
источник
@mikeserv: Да, конечно, есть много способов сделать это. Во-первых, я думаю, что я буду дублировать это с этим . Но в этом вопросе ОП хочет проверять только, он не утверждал, что хочет выйти или сообщить, если переменная не установлена, поэтому я пришел с проверкой в ​​подоболочке.
Cuonglm
2
Ну, я знаю, но это именно потому, что вы должны сделать это в подоболочке, которая показывает, что это может быть не лучшим способом тестирования здесь - это остановка действия при сбое, она не позволяет использовать какие-либо простые средства отказ. Портативно даже trapможет работать только на EXIT. Вот и все, что я говорю - это не очень хорошо подходит как проход / неудача. И я тоже не об этом говорю - я делал именно это раньше, и потребовался небольшой чат с комментариями, чтобы убедить меня. Итак, я просто думал, что заплачу.
mikeserv
1
@mikeserv: Ну, хорошо, это хороший момент. Я только задаюсь вопросом, может ли добавление, которое может заставить OP спутать с синтаксисом :)
cuonglm
2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi
Мел
источник
Я мог бы представить, что это несколько неэффективно, но очень просто и shсовместимо, а это как раз то, что мне нужно.
Hoijui
2
printf ${var+'$var exists!\n'}

... ничего не будет печатать, когда это не так. Или же...

printf $"var does%${var+.}s exist%c\n" \ not !

... скажу вам в любом случае.

вы можете использовать возвращаемое значение теста для динамического расширения до соответствующей строки формата для вашего условия:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

Вы также можете сделать printfсбой на основе замены ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... которая печатает $var does not exist!в stderr и возвращает значение, отличное от 0, когда $varне установлено, но печатает $var does exist!в stdout и возвращает 0, когда $varустановлено.

mikeserv
источник
1

Эта простая строка работает (и работает на большинстве оболочек POSIX):

${var+"false"} && echo "var is unset"

Или написано в более длинной форме:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

Расширение:

  • Если переменная имеет значение (даже ноль), ложь заменяется
  • Если переменная имеет «нет значения», то «нет значения» (ноль) заменяется.

${var+"false"}Расширения расширяет либо «нулевой» от «ложной».
Затем выполняется «ничто» или «ложь», и код выхода устанавливается.

Нет необходимости вызывать команду test( [или [[), поскольку выходное значение задается (выполнением) самого расширения.


источник
Да, за исключением некоторых старых версий zsh в эмуляции, когда $IFSсодержит f, a, l, s или e. Как и для других ответов, есть случай массивов, хэшей или других типов переменных, которые можно упомянуть.
Стефан Шазелас
@ StéphaneChazelas я написал most POSIX shells. most значитIn the greatest number of instances , не все. ... ... Итак, да, в неясном состоянии when $IFS contains f, a, l, s or eи для какой-то непонятной оболочки some old versions of zshэто терпит неудачу: какой шок !. Я должен предположить, что такая ошибка была решена давно. ... ... Вы предлагаете написать код для давно сломанных оболочек?
@ StéphaneChazelas Также: заголовок вопроса очень специфичен: Bash.
двоичная зебра ???
mikeserv
0

Чистый способ оболочки:

[ "${var+1}" ] || echo "The variable has not been set"

Тестовый скрипт:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

Результаты:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done
Андреас Микаэль Банк
источник
1
Сбой, когда переменная установлена ​​в нулевую строку.
Том Хейл,
0

С bash 4.4.19 у меня сработало следующее. Вот полный пример

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi
Афтаб Навид
источник
-1

Вы не можете использовать ifкоманду для проверки существования объявленных переменных в bash, однако -vв более новом bash существует опция, но она не переносима, и вы не можете использовать ее в более старых bashверсиях. Потому что когда вы используете переменную, если она не существует, она будет рождена одновременно.

Например, представьте, что я не использовал и не присваивал значение MYTESTпеременной, но когда вы используете команду echo, она ничего вам не показывает! Или, если вы используете if [ -z $MYTEST ]его, возвращается нулевое значение! Он не возвратил другой статус завершения, который говорит вам, что эта переменная не существует!

Теперь у вас есть два решения (без -vопции):

  1. Используя declareкоманду.
  2. Используя setкоманду.

Например:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Но, к сожалению, эти команды также показывают загруженные вами функции в памяти! Вы можете использовать declare -p | grep -q MYTEST ; echo $?команду для более чистого результата.

Сепарад Салур
источник
-1

Функция для проверки, если переменная объявлена ​​/ не установлена

в том числе пустой $array=()


В дополнение к ответу @ Gilles

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is declared";;
  *) echo "foobar is not declared";;
esac

- что я не нашел способ для инкапсулировать его в функции - Я хотел бы добавить простой вариант, который частично основан на Richard Hansen «s ответа , но делаю также обратиться к ловушке , что происходит с пустым array=():

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • Сначала проверив, установлена ​​ли (не) переменная, можно избежать вызова объявления, если в этом нет необходимости.
  • Если, однако, $1содержит имя пустого $array=(), вызов объявления объявит, что мы получим правильный результат
  • В / dev / null никогда не передается много данных, так как метод Declare вызывается только в том случае, если переменная не установлена ​​или пустой массив.


С помощью следующего кода функции могут быть протестированы:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

Скрипт должен вернуться

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset
Мартин Рюгг
источник
-1

Функция bash, которая работает как для скалярных, так и для массивов :

определение

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

вызов

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
Андрей Позолотин
источник