Як перевірити, чи існує змінна в операторі "якщо"?


69

Мені потрібно перевірити існування змінної у 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 / ...
Чіро Сантіллі新疆改造中心法轮功六四事件

Відповіді:


97

У сучасному баші (версія 4.2 і вище):

[[ -v name_of_var ]]

Від help test:

-v VAR, вірно, якщо встановлена ​​змінна оболонка VAR


3
Також працює з поодинокими дужками: [ -v name_of_var ].
meuh

7
майте на увазі, що для хешів та масивів він повертає значення false, якщо змінна не має елемента key / indice "0". Для імен, які перевіряються, він перевіряє, чи визначено ціль. Це не працює для спеціальних параметрів , як $1, $-, $#...
Stéphane Chazelas

4
Ця функція є лише у вбудованому басі 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

Це працює для скалярних змінних та інших параметрів, щоб визначити, чи змінній було присвоєно значення (порожнє чи ні, автоматично, з оточення read, призначення forчи іншого).

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

Для оболонок, які підтримують масиви, за винятком yashі zshякі не повідомлятимуться як встановлені змінні масиву, якщо не встановлено елемент індексу 0.

Для bash(але ні, ksh93ні zsh) для змінних асоціативного масиву типу , які не повідомили б про них як встановлені, якщо не встановлено їх елемент ключа "0".

Для ksh93і bashдля змінних типу nameref це істинно повертається лише тоді, коли змінна, на яку посилається 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зараз.
cas

1
@cas, ні. Не для оголошених, але не встановлених змінних. Спробуйте bash -c 'typeset -i a; typeset -p a'і порівняйте з ksh93або zsh.
Стефан Шазелас

+1 Дякуємо, що вказали мені тут. Також дивіться моє запитання unix.stackexchange.com/q/280893/674
Тім

@cas, що змінилося на bash-4.4 (вийшов у вересні 2016 року), я це відредагував.
Stéphane Chazelas

9

Як зазначено у відповіді на SO , ось спосіб перевірити:

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

де $ {somevar + x} - це розширення параметра, яке оцінюється як null, якщо var не встановлено, і замінює рядок "x" в іншому випадку.

Використовуючи -n, як запропоновано іншою відповіддю, буде перевіряти лише чи змінна містить порожній рядок. Він не перевірить своє існування.


2
Вам потрібно цитувати, $somevarщоб обробити IFS=x. Або це, або цитата x.
mikeserv

1
@mikeserv Спасибі, мені подобається дізнатися про крайові випадки :) Ти маєш на увазі if [ -z "${somevar+x}" ]? Чи все-таки потрібне цитування всередині [[та ]]?
Том Хейл

@TomHale - так, у рідкісних випадках. то [ testпроцедури приймають параметри командного рядка, так що звичайні розширення та інтерпретації як замовлено звичайним способом слід покладатися надати при виклику тесту застосовується то , що ви повинні викликати для читання , таким чином , будь-який командному рядку програми. тест {! + "!"}
mikeserv

@mikeserv Я думаю, що так, це моє 2-е питання ... Перше так, теж?
Том Хейл

1
+1; Це, мабуть, є найпростішим способом зробити це, коли set -uвоно діє, а версія Bash до 4.2.
Кайл Странд

4

POSIXly:

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

або ви можете дозволити вашій оболонці показати повідомлення для вас:

(: "${somevar?}")
zsh: somevar: parameter not set

@mikeserv: Так, звичайно, існує багато способів зробити це. Під - перше, я думаю , що я дублював його з цим . Але в цьому питанні ОП хочуть перевірити лише він, він не стверджував, що хоче вийти або повідомити, якщо змінна не встановлена, тому я прийшов з перевіркою в нижній частині.
cuonglm

2
Ну, я знаю, але саме тому, що ви повинні робити це в такій нижній частині, що вказує на те, що це може бути не найкращим способом перевірки тут - це зупинка дій щодо відмови, вона не дозволяє використовувати будь-які прості засоби провал. Портативно навіть a trapможе працювати лише на EXIT. Це все, що я говорю - він просто не застосовується як пропуск / невдача. І це я не розмовляю - я робив саме це раніше, і мені знадобилося трохи поспілкуватися, як це було, щоб переконати мене. Отже, я просто думав, що заплачу це вперед.
mikeserv

1
@mikeserv: Ну, ну, це хороший момент. Мені цікаво лише, чи додає те, що може змусити ОП плутатись із синтаксисом :)
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встановлено.


1

Ця проста лінія працює (і працює на більшості оболонок POSIX):

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

Або написано у більш довгій формі:

unset var

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

Розширення:

  • Якщо var має значення (навіть null), помилкове замінюється
  • Якщо var не має "no value", то "no value" (null) замінюється.

${var+"false"}Розширення розширює або «нульовий» від «несправжньої».
Потім виконується "нічого" або "false", і встановлюється код виходу.

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


Так, за винятком деяких старих версій zsh в sh емуляції, коли $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версіях. Тому що коли ви використовуєте змінну, якщо її немає, вона народиться одночасно.

Напр. Уявіть, що я не використовував і не призначив значення 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=()


На додаток до @ Жиля в відповідь

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, оскільки декларується, викликається лише в тому випадку, якщо змінна не встановлена ​​або порожній масив.


За допомогою наступного коду функції можна перевірити:

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