Jak sprawdzić, czy zmienna istnieje w instrukcji „if”?


69

Muszę sprawdzić istnienie zmiennej w ifinstrukcji. Coś w efekcie:

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

Najbliższym pytaniem było to , które tak naprawdę nie odpowiada na moje pytanie.


Jeśli chcesz ustawić $somevarna wartość / string jeśli zmienna nie istnieje: ${somevar:=42}.
Cyrus,

Osobiście staram się sprawdzać tylko pustkę ( [ -n "$var" ]lub [ ! -z "$var" ]). Myślę, że kontrole istnienia / nieistnienia są zbyt subtelne i wolę mój kod zgrubny i prosty.
PSkocik,

Dlaczego potrzebujesz tego kontra [ -n "$var" ]? Powiązane: stackoverflow.com/questions/3601515/…
Ciro Santilli 28 改造 中心 法轮功 六四 事件

Odpowiedzi:


97

W nowoczesnej wersji bash (wersja 4.2 i nowsze):

[[ -v name_of_var ]]

Od help test:

-v VAR, Prawda, jeśli ustawiona jest zmienna VAR powłoki


3
Współpracuje również z pojedynczych nawiasach: [ -v name_of_var ].
Meuh

7
uwaga: dla skrótów i tablic zwraca false, chyba że zmienna ma element klucz / indeks „0”. W przypadku nazwerefs sprawdza, czy cel jest zdefiniowany. To nie działa na specjalne parametry jak $1, $-, $#...
Stéphane Chazelas

4
Ta funkcja jest tylko wbudowana w bash testlub [; Nie jest dostępny w /usr/bin/test. Porównaj man testz help test.
Mark Lakata,

@MarkLakata Tak, ponieważ zewnętrzne polecenia nie mogą poznać wewnętrznego stanu powłoki.
Chris Down,

umm, czy nie zawsze powinno to być [[-v "$ name_of_var"]]?
Alexander Mills,

24

Zależy, co masz na myśli, istnieje .

Czy zmienna została zadeklarowana ale nie przypisano istnieć ?

Czy tablica (lub hash) zmienną, która została przypisana pusta lista istnieje ?

Robi nameref zmienna wskazując na zmienną, która obecnie nie jest przypisany istnieć ?

Czy uważają Państwo $-, $#, $1zmienne? (POSIX nie).

W muszlach podobnych do Bourne'a kanoniczny sposób to:

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

Że pracuje dla zmiennych skalarnych i innych parametrów, aby stwierdzić, czy zmienna została przypisana wartość (pusty lub nie, automatycznie, z otoczenia, assigments, read, forlub inne).

Dla powłok, które mają komendę typesetlub declare, które nie zgłosiłyby się jako ustawione zmienne, które zostały zadeklarowane, ale nie zostały przypisane, z wyjątkiem zsh.

Muszli, które obsługują macierze, z wyjątkiem yashi zshże nie będzie Zgłoś jako zestaw zmiennych tablicowych chyba element índice 0 został ustawiony.

Do bash(ale nie ksh93nie zsh), dla zmiennych typu asocjacyjnej , która nie zgłosić je jako zestaw , chyba że ich elementem kluczowym „0” został ustawiony.

For ksh93i bash, dla zmiennych typu nameref , zwraca wartość true tylko wtedy, gdy zmienna, do której odwołuje się nameref, jest uważana za ustawioną .

Na ksh, zshi bash, potencjalnie lepszym rozwiązaniem mogłoby być:

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

Na ksh93, zshi bash4,4 lub powyżej, istnieje również:

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

Który zgłosi zmienne, które zostały ustawione lub zadeklarowane.


1
declare -p/ też typeset -pdziała bashteraz.
cas

1
@cas, no. Nie dla deklarowanych, ale nie ustawianych zmiennych. Spróbuj bash -c 'typeset -i a; typeset -p a'i porównaj z ksh93lub zsh.
Stéphane Chazelas

+1 Dzięki za wskazanie mnie tutaj. Zobacz także moje pytanie unix.stackexchange.com/q/280893/674
Tim

@cas, który zmienił się wraz z wersją bash-4.4 (wydaną we wrześniu 2016 r.), edytowałem to w.
Stéphane Chazelas

9

Jak wspomniano w odpowiedzi na SO , oto sposób na sprawdzenie:

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

gdzie $ {somevar + x} jest rozwinięciem parametru, którego wynikiem jest null, jeśli var jest nieustawiony i w przeciwnym razie zastępuje ciąg „x”.

Użycie -n, jak sugeruje druga odpowiedź, sprawdzi tylko, czy zmienna zawiera pusty ciąg znaków. Nie sprawdzi jego istnienia.


2
Musisz zacytować, $somevaraby sobie poradzić IFS=x. Albo to, albo cytat x.
mikeserv

1
@mikeserv Dzięki, lubię uczyć się o przypadkach krawędzi :) Masz na myśli if [ -z "${somevar+x}" ]? Czy wycena nadal byłaby wymagana w środku [[i ]]?
Tom Hale,

@TomHale - tak, w rzadkich przypadkach. te [ testprocedury akceptować parametry wiersza poleceń, a więc zwykłe rozszerzenia i interpretacje jak zamawiać w zwykły sposób powinny być powołane do świadczenia na pw teście zastosowano to, co powinno doprowadzić do odczytania sposób przez każdego wiersza poleceń programu. test {! + "!"}
mikeserv

@mikeserv Chyba twoje pytanie dotyczy mojego drugiego pytania ... Czy pierwsze też jest tak?
Tom Hale,

1
+1; wydaje się, że jest to najprostszy sposób, aby to zrobić, gdy set -uobowiązuje, a wersja Bash jest wcześniejsza niż 4.2.
Kyle Strand

4

POSIXly:

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

lub możesz pozwolić swojej powłoce pokazać ci wiadomość:

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

@mikeserv: Tak, oczywiście, jest na to wiele sposobów. Po pierwsze, myślę, że powielę to z tym . Ale w tym pytaniu OP chce tylko sprawdzić, nie twierdził, że chce wyjść lub zgłosić, jeśli zmienna jest rozbrojona, więc przyszedłem z podprawką sprawdzania.
cuonglm

2
Cóż, wiem, ale to właśnie dlatego, że musisz to zrobić w takiej podpowłoce, co oznacza, że ​​może nie być to najlepszy sposób na przetestowanie tutaj - jest to akcja zatrzymania w przypadku awarii, nie pozwala na żaden prosty sposób obsługi porażka. Przenośny nawet trapmoże działać tylko na EXIT. To wszystko, co mówię - po prostu nie ma zastosowania jako zaliczenie / niezaliczenie. I to nie ja też mówię - zrobiłem to już wcześniej i potrzebowałem krótkiego komentarza na czacie, aby mnie przekonać. Pomyślałem, że zapłacę to dalej.
mikeserv

1
@mikeserv: Cóż, to dobra uwaga. Zastanawiam się tylko, czy dodawanie może sprawić, że OP może pomylić ze składnią :)
cuonglm

2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi

Wyobrażam sobie, że jest to nieco nieefektywne, ale jest bardzo proste i shkompatybilne, a tego właśnie potrzebuję.
hoijui

2
printf ${var+'$var exists!\n'}

... nic nie wydrukuje, gdy nie będzie. Lub...

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

... powie ci tak czy inaczej.

możesz użyć wartości zwracanej testu, aby dynamicznie rozwinąć ciąg znaków do odpowiedniego formatu dla swojego stanu:

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

Możesz również printfzawieść na podstawie zamiany ...

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

... który wypisuje $var does not exist!na stderr i zwraca wartość inną niż 0, gdy $varjest rozbrojony, ale drukuje $var does exist!na stdout i zwraca 0, gdy $varjest ustawiony.


1

Ta prosta linia działa (i działa na większości powłok POSIX):

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

Lub, napisane w dłuższej formie:

unset var

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

Rozszerzenie to:

  • Jeśli var ma wartość (nawet zero), false jest zastępowane
  • Jeśli var ma „brak wartości”, wówczas „brak wartości” (null) jest zastępowane.

${var+"false"}Ekspansja rozszerza albo „null” na „false”.
Następnie wykonywane jest „nic” lub „fałsz” i ustawiany jest kod wyjścia.

Nie ma potrzeby wywoływania polecenia test( [lub [[), ponieważ wartość wyjścia jest ustawiana przez (wykonanie) samego rozszerzenia.


Tak, z wyjątkiem niektórych starych wersji emulacji zsh w sh, gdy $IFSzawiera f, a, l, s lub e. Podobnie jak w przypadku innych odpowiedzi, mamy do czynienia z tablicami, skrótami lub innymi typami zmiennych, o których warto wspomnieć.
Stéphane Chazelas,

@ StéphaneChazelas napisałem most POSIX shells. most znaczyIn the greatest number of instances nie wszystko. ... ... Tak, w niejasnym stanie when $IFS contains f, a, l, s or ei dla jakiejś niejasnej powłoki some old versions of zshto się nie udaje: co za szok! Powinienem założyć, że taki błąd został rozwiązany dawno temu. ... ... Czy proponujesz, że musimy napisać kod dla już dawno zepsutych powłok?

@ StéphaneChazelas Także: Tytuł pytania jest bardzo konkretny: Bash.

binarna zebra ???
mikeserv

0

Czysty sposób:

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

Skrypt testowy:

#!/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"

Wyniki:

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
Nie działa, gdy zmienna jest ustawiona na ciąg pusty.
Tom Hale,

0

W wersji bash 4.4.19 działało dla mnie następujące. Oto kompletny przykład

$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

Nie można użyć ifpolecenia do sprawdzenia istnienia deklarowanych zmiennych w bash, jednak -vopcja istnieje w nowszym bash, ale nie jest przenośna i nie można jej używać w starszych bashwersjach. Ponieważ kiedy używasz zmiennej, jeśli ona nie istnieje, narodzi się w tym samym czasie.

Np. Wyobraź sobie, że nie użyłem ani nie przypisałem wartości do MYTESTzmiennej, ale kiedy używasz polecenia echo, nic ci to nie pokazuje! Lub jeśli if [ -z $MYTEST ]go używasz zwrócił wartość zerową! Nie zwrócił innego statusu wyjścia, co oznacza, że ​​ta zmienna nie istnieje!

Teraz masz dwa rozwiązania (bez -vopcji):

  1. Za pomocą declarepolecenia.
  2. Za pomocą setpolecenia.

Na przykład:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Ale niestety te polecenia pokazują również załadowane funkcje w pamięci! Możesz użyć declare -p | grep -q MYTEST ; echo $?polecenia, aby uzyskać czystszy wynik.


-1

Funkcja sprawdzająca, czy zmienna jest zadeklarowana / rozbrojona

w tym pusty $array=()


Oprócz @ Gilles za odpowiedź

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

- czego nie znaleźć sposób, aby ująć go w obrębie funkcji - chciałbym dodać prostą wersję, która jest częściowo oparte na Richard Hansen „s odpowiedzi , ale odnosi się również pułapki, który występuje z pustym 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;} 
}
  • Najpierw sprawdzając, czy zmienna jest (nie) ustawiona, można uniknąć wywołania deklaracji, jeśli nie jest to konieczne.
  • Jeśli jednak $1zawiera nazwę pustego $array=(), wywołanie deklaracji upewni się, że otrzymamy właściwy wynik
  • Nigdy nie przekazuje się zbyt wielu danych do / dev / null, ponieważ deklaracja jest wywoływana tylko wtedy, gdy zmienna jest nieustawiona lub pusta tablica.


Za pomocą następującego kodu można przetestować funkcje:

( # 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"
)

Skrypt powinien wrócić

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

funkcja bash, która działa zarówno na typy skalarne, jak i tablicowe :

definicja

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
}

wezwanie

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.