Jak sprawdzić, czy zmienna jest ustawiona w Bash?

1566

Skąd mam wiedzieć, czy zmienna jest ustawiona w Bash?

Na przykład, jak sprawdzić, czy użytkownik podał pierwszy parametr funkcji?

function a {
    # if $1 is set ?
}
prosseek
źródło
12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens
210
Uwaga dla osób poszukujących rozwiązania: Istnieje wiele wysoko ocenianych odpowiedzi na to pytanie, które odpowiadają na pytanie „zmienna niepusta”. Więcej rozwiązań korekcyjnych („jest zestaw zmiennych”) wymieniono w odpowiedziach Jensa i Lionela poniżej.
Nathan Kidd
8
Również Russell Harmon i Seamus mają rację ze swoim -vtestem, chociaż wydaje się, że jest on dostępny tylko w nowych wersjach bashi nie jest przenośny między powłokami.
Graeme
5
Jak zauważył @NathanKidd, Lionel i Jens podają prawidłowe rozwiązania. prosseek, powinieneś zmienić zaakceptowaną odpowiedź na jedną z nich.
Garrett
3
... lub niepoprawna odpowiedź może zostać zanegowana przez bardziej wymagających wśród nas, ponieważ @prosseek nie rozwiązuje problemu.
dan3

Odpowiedzi:

2299

(Zwykle) Właściwy sposób

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

gdzie ${var+x}jest rozwinięciem parametru, który zwraca wartość do varzera, jeśli jest rozbrojony, i w xprzeciwnym razie zastępuje ciąg .

Cytaty Dygresja

Cytaty można pominąć (więc możemy powiedzieć ${var+x}zamiast "${var+x}"), ponieważ ta składnia i użycie gwarantuje, że rozwinie się tylko do czegoś, co nie wymaga cudzysłowów (ponieważ albo rozwija się do x(który nie zawiera podziałów słów, więc nie potrzebuje cudzysłowów), lub nic (co skutkuje [ -z ], co dogodnie daje taką samą wartość (prawda), [ -z "" ]co również).

Jednak chociaż cytaty można bezpiecznie pominąć i nie było to od razu oczywiste dla wszystkich (nie było nawet oczywiste dla pierwszego autora wyjaśnienia cytatów, który jest również głównym koderem Bash), czasem lepiej byłoby napisać rozwiązanie z cytatami, jak [ -z "${var+x}" ]przy bardzo niewielkim możliwym koszcie kary prędkości O (1). Pierwszy autor dodał również to jako komentarz obok kodu, używając tego rozwiązania, podając adres URL tej odpowiedzi, który teraz zawiera również wyjaśnienie, dlaczego cytaty można bezpiecznie pominąć.

(Często) Niewłaściwy sposób

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

Jest to często błędne, ponieważ nie rozróżnia zmiennej, która jest nieustawiona, a zmiennej, która jest ustawiona na pusty ciąg. To znaczy, jeśli var=''wtedy powyższe rozwiązanie wyświetli „var is blank”.

Rozróżnienie między nieustawionym a „ustawionym na pusty ciąg” jest niezbędne w sytuacjach, w których użytkownik musi określić rozszerzenie lub dodatkową listę właściwości, a nieokreślenie ich domyślnie przyjmuje niepustą wartość, podczas gdy określenie pustego ciągu powinno spraw, aby skrypt używał pustego rozszerzenia lub listy dodatkowych właściwości.

To rozróżnienie może nie być konieczne w każdym scenariuszu. W takich przypadkach [ -z "$var" ]będzie dobrze.

Lionel
źródło
29
@Garrett, twoja edycja spowodowała, że ​​ta odpowiedź jest niepoprawna, ${var+x}to poprawne zastąpienie do użycia. Użycie [ -z ${var:+x} ]nie daje innego wyniku niż [ -z "$var" ].
Graeme
11
To nie działa Otrzymuję komunikat „nieustawione” niezależnie od tego, czy zmienna jest ustawiona na wartość, czy nie (wyczyszczone przez „unset var”, „echo $ var” nie generuje danych wyjściowych).
Brent212,
10
W przypadku składni rozwiązania $ {parametr + słowo} oficjalna sekcja podręcznika to gnu.org/software/bash/manual/… ; jednak błąd w tym, że nie bardzo wyraźnie wspomina o tej składni, ale mówi tylko cytat (Pominięcie dwukropka [":"] powoduje test tylko dla parametru, który nie jest ustawiony. .. $ {parametr: + słowo} [oznacza] Jeśli parametr jest zerowy lub nieustawiony, nic nie jest podstawiane, w przeciwnym razie rozszerzenie wyrazu jest podstawiane.); cytowany odnośnik pubs.opengroup.org/onlinepubs/9699919799/utilities/... (dobrze wiedzieć) ma tutaj znacznie jaśniejsze dokumenty.
Destiny Architect
7
Wygląda na to, że cytaty są wymagane, gdy używasz wielu wyrażeń, np [ -z "" -a -z ${var+x} ] Dostaje się bash: [: argument expecteddo bash 4.3-ubuntu (ale nie np. Zsh). Naprawdę nie lubię odpowiedzi, używając składni, która w niektórych przypadkach działa.
FauxFaux,
41
Użycie prostego [ -z $var ]nie jest bardziej „błędne” niż pominięcie cudzysłowu. Tak czy inaczej, przyjmujesz założenia dotyczące wkładu. Jeśli dobrze traktujesz pusty ciąg jako nieuzbrojony, [ -z $var ]wystarczy.
nshew
909

Aby sprawdzić zmienną łańcuchową o wartości innej niż zero / zero, tj. Jeśli jest ustawiona, użyj

if [ -n "$1" ]

To jest przeciwieństwo -z. Używam -nwięcej niż -z.

Użyłbyś tego w następujący sposób:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi
mbrannig
źródło
86
Zwykle wolę [[ ]]ponad [ ], ponieważ [[ ]]jest bardziej wydajny i powoduje mniej problemów w niektórych sytuacjach (zobacz to pytanie, aby wyjaśnić różnicę między nimi). Pytanie dotyczy w szczególności rozwiązania bash i nie wspomina o żadnych wymaganiach dotyczących przenośności.
Flow
18
Pytanie brzmi, jak sprawdzić, czy zmienna jest ustawiona . Wtedy nie możesz założyć, że zmienna jest ustawiona.
HelloGoodbye,
7
Zgadzam się, []działa lepiej zwłaszcza w przypadku skryptów powłoki (non-bash).
Hengjie
73
Nie działa set -u.
Ciro Santilli 10 病毒 审查 六四 事件 法轮功
63
Pamiętaj, że -njest to test domyślny, więc prościej [ "$1" ]lub [[ $1 ]]też działaj. Należy również pamiętać, że przy [[ ]] cytowaniu nie jest konieczne .
tne
478

Oto jak sprawdzić, czy parametr jest nieustawiony , pusty („Null”) lub ustawiony za pomocą wartości :

+--------------------+----------------------+-----------------+-----------------+
|   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 |
+--------------------+----------------------+-----------------+-----------------+

Źródło: POSIX: Rozszerzenie parametrów :

We wszystkich przypadkach pokazanych jako „podstawienie” wyrażenie jest zastępowane pokazaną wartością. We wszystkich przypadkach pokazanych jako „przypisać” parametrowi przypisywana jest ta wartość, która również zastępuje wyrażenie.

Aby pokazać to w akcji:

+--------------------+----------------------+-----------------+-----------------+
|   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           | ""              |
+--------------------+----------------------+-----------------+-----------------+
Jens
źródło
5
@HelloGoodbye Tak to działa: set foo; echo ${1:-set but null or unset}echos "foo"; set --; echo ${1:-set but null or unset}echa ustawione, ale zerowe ...
Jens
4
@HelloGoodbye pozycyjne parametry można ustawić, uh, set:-)
Jens
95
Ta odpowiedź jest bardzo myląca. Jakieś praktyczne przykłady korzystania z tego stołu?
Ben Davis,
12
@BenDavis Szczegóły wyjaśniono w linku do standardu POSIX, który odwołuje się do rozdziału, z którego pochodzi tabela. Jedną z konstrukcji, których używam w prawie każdym skrypcie, który piszę, jest : ${FOOBAR:=/etc/foobar.conf}ustawienie domyślnej wartości zmiennej, jeśli jest ona nieustawiona lub zerowa.
Jens
1
parameterto dowolna nazwa zmiennej. wordto ciąg znaków, który należy zastąpić w zależności od używanej składni w tabeli i tego, czy zmienna $parameterjest ustawiona, ustawiona, ale pusta, czy też nie. Nie komplikuje to, ale wordmoże również zawierać zmienne! Może to być bardzo przydatne do przekazywania opcjonalnych argumentów oddzielonych znakiem. Na przykład: ${parameter:+,${parameter}}wyświetla przecinek oddzielony, ,$parameterjeśli jest ustawiony, ale nie ma wartości null. W tym przypadku wordjest,${parameter}
TrinitronX
260

Chociaż większość podanych tu technik jest poprawna, bash 4.2 obsługuje rzeczywisty test na obecność zmiennej ( man bash ), zamiast testować wartość zmiennej.

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

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

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

Warto zauważyć, że to podejście nie spowoduje błędu, gdy zostanie użyte do sprawdzenia, czy zmienna nie jest ustawiona w trybie set -u/ set -o nounset, w przeciwieństwie do wielu innych podejść, takich jak używanie [ -z.

Russell Harmon
źródło
9
W wersji bash 4.1.2, niezależnie od tego, czy zmienna jest ustawiona, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan
32
Argument „-v” do wbudowanego „testu” został dodany w bash 4.2.
Ti Strga
19
argument -v został dodany jako uzupełnienie opcji powłoki -u (rzeczownik). Przy włączonym 'rzeczowniku' (set -u) powłoka zwróci błąd i zakończy działanie, jeśli nie będzie interaktywna. $ # było dobre do sprawdzania parametrów pozycyjnych. Jednak nazwane zmienne potrzebowały innego sposobu, niż clobbering, aby zidentyfikować je jako nieustawione. To niefortunne, że ta funkcja pojawiła się tak późno, ponieważ wielu z nich musi być kompatybilnych z wcześniejszymi wersjami, w którym to przypadku nie mogą jej używać. Jedyną inną opcją jest użycie sztuczek lub „hacków”, aby obejść to, jak pokazano powyżej, ale jest to najbardziej nieeleganckie.
osirisgothra
2
Zauważ, że to nie działa dla zmiennych, które są zadeklarowane, ale nie są ustawione. np.declare -a foo
miken32
17
Dlatego wciąż czytam po odpowiedzi zaakceptowanej / najwyżej głosowanej.
Joe
176

Można to zrobić na wiele sposobów, jednym z nich jest:

if [ -z "$1" ]

To się powiedzie, jeśli 1 $ jest zerowy lub nieustawiony

ennuikiller
źródło
46
Istnieje różnica między parametrem nieustawionym a parametrem o wartości zerowej.
chepner
21
Chcę tylko być pedantyczny i wskazać, że jest to odpowiedź przeciwna do tego, co stawia pytanie. Pytania dotyczą tego, czy zmienna JEST ustawiona, a nie, czy zmienna nie jest ustawiona.
Philluminati,
47
[[ ]]jest niczym innym jak pułapką przenośności. if [ -n "$1" ]należy go tutaj użyć.
Jens
73
Ta odpowiedź jest niepoprawna. Pytanie dotyczy sposobu ustalenia, czy zmienna jest ustawiona, a nie czy jest ustawiona na niepustą wartość. Biorąc pod uwagę foo="", $fooma wartość, ale -zpo prostu zgłosi, że jest pusta. I -z $foowybuchnie, jeśli masz set -o nounset.
Keith Thompson,
4
Zależy to od definicji „ustawiona zmienna”. Jeśli chcesz sprawdzić, czy zmienna jest ustawiona na niezerowy ciąg, odpowiedź mbranning jest poprawna. Ale jeśli chcesz sprawdzić, czy zmienna jest zadeklarowana, ale nie zainicjowana (tj. foo=), To odpowiedź Russela jest poprawna.
Flow
76

Zawsze znajduję tabelę POSIX w drugiej odpowiedzi wolną od grokowania, więc oto moje zdanie:

   +----------------------+------------+-----------------------+-----------------------+
   |   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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Zauważ, że każda grupa (z poprzedzającym dwukropkiem i bez niego) ma takie same ustawione i nieuzbrojone przypadki, więc jedyną różnicą jest sposób obsługi pustych przypadków.

W poprzednim dwukropku puste i nieuzbrojone przypadki są identyczne, więc użyłbym tych tam, gdzie to możliwe (tj. Używałbym :=nie tylko =, ponieważ pusty przypadek jest niespójny).

Nagłówki:

  • zestaw oznacza, że VARIABLEnie jest pusty ( VARIABLE="something")
  • puste oznacza, że VARIABLEjest puste / null ( VARIABLE="")
  • unset oznacza, VARIABLEże nie istnieje ( unset VARIABLE)

Wartości:

  • $VARIABLE oznacza, że ​​wynikiem jest pierwotna wartość zmiennej.
  • "default" oznacza, że ​​wynikiem był podany ciąg zastępczy.
  • "" oznacza, że ​​wynik ma wartość NULL (pusty ciąg).
  • exit 127 oznacza, że ​​skrypt przestaje działać z kodem wyjścia 127.
  • $(VARIABLE="default")oznacza, że ​​wynikiem jest oryginalna wartość zmiennej, a podany ciąg zastępujący jest przypisywany do zmiennej do wykorzystania w przyszłości.
deizel
źródło
1
Naprawiono tylko te, które są rzeczywistymi błędami kodowania (przypadki niepożądanej pośredniczości podczas pracy ze zmiennymi). Dokonałem edycji, aby naprawić kilka innych przypadków, w których dolar robi różnicę w interpretacji opisu. BTW, wszystkie zmienne powłoki (z wyjątkiem tablic) są zmiennymi łańcuchowymi; może się zdarzyć, że zawierają ciąg, który można interpretować jako liczbę. Mówienie o ciągach tylko zamienia komunikat. Cytaty nie mają nic wspólnego z ciągami, są tylko alternatywą dla ucieczki. VARIABLE=""można zapisać jako VARIABLE=. Mimo to ten pierwszy jest bardziej czytelny.
Palec
Dzięki za tabelę jest bardzo przydatna, ale staram się, aby skrypt zakończył działanie, jeśli jest rozbrojony lub pusty. Ale kod wyjścia, który otrzymuję, to 1, a nie 127.
Astronauta
64

Aby zobaczyć, czy zmienna nie jest pusta, używam

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

Odwrotny test sprawdza, czy zmienna jest rozbrojona lub pusta:

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

Aby sprawdzić, czy zmienna jest ustawiona (pusta czy niepusta), używam

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

Przeciwnie, jeśli zmienna jest rozbrojona:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments
phkoester
źródło
Ja także używałem tego od dłuższego czasu; tylko w mniejszym stopniu:[ $isMac ] && param="--enable-shared"
@Bhaskar Myślę, że dzieje się tak, ponieważ ta odpowiedź już podała w zasadzie tę samą odpowiedź ( ustawiono opcję ${variable+x}uzyskiwania xiff $variable) prawie pół roku wcześniej. Ma też znacznie więcej szczegółów wyjaśniających, dlaczego tak jest.
Mark Haferkamp
ale ${variable+x}jest mniej czytelny (i oczywisty)
Knocte
35

W nowoczesnej wersji Bash (chyba 4.2 lub nowszej; nie wiem na pewno), spróbowałbym:

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
Seamus Connor
źródło
5
Zauważ też, że [ -v "$VARNAME" ]nie jest to niepoprawne, ale po prostu wykonuje inny test. Załóżmy VARNAME=foo; następnie sprawdza, czy jest ustawiona zmienna o nazwie foo.
chepner
5
Uwaga dla tych, którzy chcą -vmieć przenośność, nie jest zgodny z POSIX
kzh
Jeśli się dostanie [: -v: unexpected operator, trzeba zapewnić, aby użyć bashzamiast sh.
koppor
25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Chociaż w przypadku argumentów zwykle najlepiej jest przetestować $ #, czyli, moim zdaniem, liczbę argumentów.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi
Paul Creasey
źródło
1
Pierwszy test jest wstecz; Myślę, że chcesz [ "$1" != "" ](lub [ -n "$1" ]) ...
Gordon Davisson
10
Nie powiedzie się, jeśli $1zostanie ustawiony pusty ciąg.
HelloGoodbye
2
Druga część odpowiedzi (parametry zliczania) jest użytecznym obejściem dla pierwszej części odpowiedzi, która nie działa, gdy $1jest ustawiona na pusty ciąg.
Mark Berry
To się nie powiedzie, jeśli set -o nounsetjest włączone.
Flimm
21

Uwaga

Daję odpowiedź bardzo skoncentrowaną na Bash z powodu bashtagu.

Krótka odpowiedź

Tak długo, jak masz do czynienia z nazwanymi zmiennymi w Bash, ta funkcja powinna zawsze informować, czy zmienna została ustawiona, nawet jeśli jest to pusta tablica.

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

Dlaczego to działa

W Bash (przynajmniej do 3.0), jeśli varjest zmienną zadeklarowaną / ustawioną, wówczas declare -p varwypisuje declarepolecenie, które ustawi zmienną varna dowolny jej aktualny typ i wartość, i zwraca kod statusu 0(sukces). Jeśli nie varjest zadeklarowany, declare -p varwyświetla komunikat o błędzie stderri zwraca kod stanu 1. Użycie &>/dev/nullprzekierowuje zarówno zwykłe, jak stdouti stderrwyjściowe /dev/null, nigdy nie widoczne i bez zmiany kodu statusu. Dlatego funkcja zwraca tylko kod statusu.

Dlaczego inne metody (czasami) zawodzą w Bash

  • [ -n "$var" ]: Sprawdza tylko, czy ${var[0]}jest niepuste. (W Bash $varjest taki sam jak ${var[0]}.)
  • [ -n "${var+x}" ]: To sprawdza tylko, czy ${var[0]}jest ustawione.
  • [ "${#var[@]}" != 0 ]: Sprawdza to tylko, czy $varustawiony jest przynajmniej jeden indeks .

Gdy ta metoda zawiedzie w Bash

To działa tylko dla nazwanych zmiennych (włącznie $_) nie pewnych zmiennych specjalnych ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., a wszelkie mogę zapomnieć). Ponieważ żadna z nich nie jest tablicami, styl POSIX [ -n "${var+x}" ]działa dla wszystkich tych zmiennych specjalnych. Ale uważaj na zawijanie go w funkcję, ponieważ wiele specjalnych zmiennych zmienia wartości / istnienie podczas wywoływania funkcji.

Uwaga dotycząca zgodności z powłoką

Jeśli twój skrypt ma tablice i próbujesz uczynić go kompatybilnym z jak największą liczbą powłok, rozważ użycie typeset -pzamiast niego declare -p. Czytałem, że ksh obsługuje tylko te pierwsze, ale nie byłem w stanie tego przetestować. Wiem, że Bash 3.0+ i Zsh 5.5.1 obsługują oba typeset -pi declare -próżnią się tylko tym, w którym jedno jest alternatywą dla drugiego. Ale nie testowałem różnic poza tymi dwoma słowami kluczowymi i nie testowałem innych powłok.

Jeśli potrzebujesz, aby skrypt był zgodny z POSIX sh, nie możesz używać tablic. Bez tablic [ -n "{$var+x}" ]działa.

Kod porównawczy dla różnych metod w Bash

Ta funkcja resetuje zmienną var, evals przekazany kod, uruchamia testy w celu ustalenia, czy varjest ustawiona przez evalkod d, a na koniec wyświetla wynikowe kody stanu dla różnych testów.

Ja omijając test -v var, [ -v var ]i [[ -v var ]]ponieważ dają one takie same wyniki ze standardem POSIX [ -n "${var+x}" ], wymagając Basha wersji 4.2 lub nowszej. Pomijam również, typeset -pponieważ jest taki sam, jak declare -pw testowanych przeze mnie powłokach (Bash 3.0 do 5.0 i 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
}

Kod przypadku testowego

Zauważ, że wyniki testu mogą być nieoczekiwane z powodu traktowania przez Bash indeksów tablic nienumerycznych jako „0”, jeśli zmienna nie została zadeklarowana jako tablica asocjacyjna. Również tablice asocjacyjne działają tylko w 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

Wyjście testowe

W mnemoniki badań odpowiadają w wierszu nagłówka, aby [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ]i declare -p var, odpowiednio.

                         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

Podsumowanie

  • declare -p var &>/dev/null jest (100%?) niezawodny do testowania nazwanych zmiennych w Bash od co najmniej 3.0.
  • [ -n "${var+x}" ] jest niezawodny w sytuacjach zgodnych z POSIX, ale nie obsługuje tablic.
  • Istnieją inne testy do sprawdzania, czy zmienna nie jest pusta, oraz do sprawdzania deklarowanych zmiennych w innych powłokach. Ale testy te nie są odpowiednie dla skryptów Bash ani POSIX.
Mark Haferkamp
źródło
3
Z odpowiedzi, które próbowałem, ta ( declare -p var &>/dev/null) jest TYLKO tą, która działa. DZIĘKUJĘ CI!
user1387866
2
@ mark-haferkamp Próbowałem edytować twoją odpowiedź, aby dodać krytyczne poprzedzające „/” do kodu funkcji, więc czyta, ... &> /dev/nullale SO nie pozwala mi na edycję pojedynczego znaku 🙄 (musi być> 6 znaków - nawet próbowałem dodać białe znaki ale to nie działało). Warto również zmienić funkcję, aby przełączyć się $1na próbkę o nazwie zmienna (np. OUTPUT_DIR), Ponieważ, jak zauważyłeś, specjalne zmienne, takie jak $1, nie działają tutaj. W każdym razie jest to zmiana krytyczna, w przeciwnym razie kod chce utworzyć plik o nazwie nullw katalogu względnym o nazwie dev. BTW - świetna odpowiedź!
JoeHanna
Działa także użycie nazwy zmiennej bez przedrostka $: declare -p PATH &> /dev/nullzwraca 0, gdzie declare -p ASDFASDGFASKDF &> /dev/nullzwraca 1. 1. (w wersji bash 5.0.11 (1))
joehanna
1
Świetna robota! 1 słowo przestrogi: declare vardeklaruje zmienną var, ale nie ustawia jej pod względem set -o nounset: Testuj za pomocądeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche
@joehanna Dzięki za złapanie tego zaginionego /! Naprawiłem to, ale myślę, że logika jest wystarczająco myląca, aby uzasadnić działanie funkcji, szczególnie w świetle komentarza @ xebeche. @ xebeche To jest niewygodne dla mojej odpowiedzi… Wygląda na to, że będę musiał spróbować czegoś takiego jak declare -p "$1" | grep =lub test -v "$1".
Mark Haferkamp
19

Dla tych, którzy chcą sprawdzić, czy nie jest ustawiony lub pusty w skrypcie z set -u:

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

Regularne [ -z "$var" ]sprawdzanie zakończy się niepowodzeniem z var; unbound variableif, set -uale [ -z "${var-}" ] rozwija się do pustego łańcucha, jeśli varjest rozbrojony bez niepowodzenia.

RubenLaguna
źródło
Brakuje dwukropka. To powinno być ${var:-}, nie ${var-}. Ale w Bash mogę potwierdzić, że działa nawet bez dwukropka.
Palec
3
${var:-}i ${var-}oznaczają różne rzeczy. ${var:-}rozwinie się do pustego ciągu, jeśli jest varrozbrojony lub zerowy, i ${var-}rozwinie się do pustego ciągu, tylko jeśli zostanie rozbrojony.
RubenLaguna
Właśnie nauczyłem się czegoś nowego, dzięki! Pominięcie dwukropka powoduje tylko test parametru, który nie jest ustawiony. Innymi słowy, jeśli dwukropek jest uwzględniony, operator sprawdza istnienie obu parametrów i czy jego wartość nie jest zerowa; jeśli dwukropek zostanie pominięty, operator sprawdza tylko istnienie. Tutaj nie ma znaczenia, ale dobrze wiedzieć.
Palec
17

Chcesz wyjść, jeśli jest rozbrojony

To zadziałało dla mnie. Chciałem, aby mój skrypt zakończył działanie z komunikatem o błędzie, jeśli parametr nie został ustawiony.

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

Zwraca to z błędem po uruchomieniu

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

Zaznacz tylko, bez wyjścia - puste i rozbrojone są NIEPRAWIDŁOWE

Wypróbuj tę opcję, jeśli chcesz tylko sprawdzić, czy wartość set = VALID lub 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

Lub nawet krótkie testy ;-)

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

Zaznacz tylko, bez wyjścia - Tylko puste jest NIEPRAWIDŁOWE

I to jest odpowiedź na pytanie. Użyj tego, jeśli chcesz tylko sprawdzić, czy wartość set / empty = VALID lub unset = INVALID.

UWAGA, „1” w „..- 1}” jest nieznaczne, może być dowolne (np. 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

Krótkie testy

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

Dedykuję tę odpowiedź @ mklement0 (komentarze), która wezwała mnie do dokładnej odpowiedzi na pytanie.

Odniesienie http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02

Jarrod Chesney
źródło
15

Aby sprawdzić, czy zmienna jest ustawiona na niepustą wartość, użyj [ -n "$x" ], jak wskazali już inni.

Przez większość czasu dobrym pomysłem jest traktowanie zmiennej, która ma pustą wartość w ten sam sposób, jak zmiennej, która jest nieuzbrojona. Ale możesz je rozróżnić, jeśli potrzebujesz: [ -n "${x+set}" ]( "${x+set}"rozwija się do setif xjest ustawiony i do pustego ciągu jeśli xjest nieustawiony).

Aby sprawdzić, czy parametr został przekazany, przetestuj $#, czyli liczbę parametrów przekazanych do funkcji (lub do skryptu, gdy nie ma jej w funkcji) (patrz odpowiedź Paula ).

Gilles „SO- przestań być zły”
źródło
14

Przeczytaj sekcję „Rozszerzanie parametrów” na bashstronie man. Rozszerzanie parametrów nie zapewnia ogólnego testu dla ustawianej zmiennej, ale istnieje kilka rzeczy, które można zrobić z parametrem, jeśli nie jest ustawiony.

Na przykład:

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

ustawi wartość first_argrówną, $1jeśli jest przypisany, w przeciwnym razie użyje wartości „foo”. Jeśli aabsolutnie konieczne jest przyjęcie pojedynczego parametru i nie istnieje żaden dobry parametr domyślny, można wyjść z komunikatem o błędzie, gdy nie podano żadnego parametru:

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

(Zwróć uwagę na użycie :jako polecenia zerowego, które po prostu rozszerza wartości jego argumentów. Nie chcemy nic robić $1w tym przykładzie, po prostu wyjdź, jeśli nie jest ustawione)

chepner
źródło
1
Jestem zaskoczony, że żadna z pozostałych odpowiedzi nie wspomina o prostocie i elegancji : ${var?message}.
tripleee
Skąd to wziąłeś? Wspaniale! To po prostu działa! I jest krótki i elegancki! Dziękuję Ci!
Roger,
13

W bash możesz używać -vwewnątrz [[ ]]wbudowanego:

#! /bin/bash -u

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

echo $SOMEVAR
AlejandroVD
źródło
8

Użycie [[ -z "$var" ]]jest najłatwiejszym sposobem na sprawdzenie, czy zmienna została ustawiona, czy nie, ale ta opcja -znie rozróżnia zmiennej nieuzbrojonej od zmiennej ustawionej na pusty ciąg:

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

Najlepiej sprawdzić to zgodnie z rodzajem zmiennej: zmienna env, parametr lub zmienna regularna.

W przypadku zmiennej env:

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

W przypadku parametru (na przykład w celu sprawdzenia istnienia parametru $5):

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

W przypadku zmiennej regularnej (używając funkcji pomocniczej, aby zrobić to w elegancki sposób):

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

Uwagi:

  • $#: podaje liczbę parametrów pozycyjnych.
  • declare -p: podaje definicję zmiennej przekazywanej jako parametr. Jeśli istnieje, zwraca 0, jeśli nie, zwraca 1 i wyświetla komunikat o błędzie.
  • &> /dev/null: pomija wyjście z declare -pbez wpływu na jego kod powrotu.
Peregring-Łk
źródło
5

Powyższe odpowiedzi nie działają, gdy set -uwłączona jest opcja Bash . Nie są też dynamiczne, np. Jak testować zmienną o nazwie „manekin” jest zdefiniowany? Spróbuj tego:

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.
}

Powiązane: W Bash, jak sprawdzić, czy zmienna jest zdefiniowana w trybie „-u”

kevinarpe
źródło
1
Bardzo chciałbym wiedzieć, dlaczego ta odpowiedź została odrzucona ... Działa świetnie dla mnie.
LavaScornedOven
W Bash, można zrobić to samo bez eval: zmiany eval "[ ! -z \${$1:-} ]"do oceny pośredniej: [ ! -z ${!1:-} ];.
@BinaryZebra: Ciekawy pomysł. Proponuję napisać jako osobną odpowiedź.
kevinarpe
Użycie „eval” powoduje, że to rozwiązanie jest podatne na wstrzykiwanie kodu: $ is_var_defined 'x}]; echo „gotcha”> & 2; # 'gotcha
tetsujin
4

Możesz to zrobić:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}
kodaddict
źródło
9
Lub możesz to zrobić -ni nie negować -z.
Wstrzymano do odwołania.
1
potrzebujesz cytatów $1, tj [ ! -z "$1" ]. Lub możesz pisać [[ ! -z $1 ]]w bash / ksh / zsh.
Gilles „SO- przestań być zły”
5
Nie powiedzie się, jeśli $1zostanie ustawiony pusty ciąg.
HelloGoodbye
4

Mój preferowany sposób to:

$ 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

Zasadniczo więc, jeśli zmienna jest ustawiona, staje się „zaprzeczeniem wynikowego false ” (to, co będzie true= „jest ustawione”).

A jeśli jest rozbrojony, stanie się „zaprzeczeniem wynikowego true” (jak ocenia pusty wynik true) (więc zakończy się jako false„= NIE ustawiony”).

Moc Wodnika
źródło
1
[[ $foo ]]

Lub

(( ${#foo} ))

Lub

let ${#foo}

Lub

declare -p foo
Steven Penny
źródło
1

W powłoce możesz użyć -zoperatora, który ma wartość True, jeśli długość łańcucha wynosi zero.

Prosty jednowarstwowy do ustawienia domyślnego, MY_VARjeśli nie jest ustawiony, w przeciwnym razie opcjonalnie możesz wyświetlić komunikat:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
kenorb
źródło
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
solidsnack
źródło
$1, $2A nawet $Nzawsze jest ustawiony. Przepraszamy, to nie powiodło się.
Próbowałem i nie działało, być może moja wersja bash nie obsługuje tego. Idk, przepraszam, jeśli się mylę. :)
0

Znalazłem (znacznie) lepszy kod do zrobienia tego, jeśli chcesz sprawdzić coś w $@.

jeśli [[$ 1 = ""]]
następnie
  echo „$ 1 jest pusty”
jeszcze
  echo „$ 1 jest wypełniony”
fi

Dlaczego to wszystko? Wszystko $@istnieje w bash, ale domyślnie jest to pusty, więc test -zi test -nnie mógł pomóc.

Aktualizacja: Możesz również policzyć liczbę znaków w parametrach.

jeśli [$ {# 1} = 0]
następnie
  echo „$ 1 jest pusty”
jeszcze
  echo „$ 1 jest wypełniony”
fi

źródło
Wiem, ale to wszystko. Jeśli coś jest puste, pojawi się błąd! Przepraszam. : P
0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
Graham
źródło
11
Witamy w StackOverflow. Doceniając twoją odpowiedź, najlepiej podać więcej niż tylko kod. Czy możesz dodać uzasadnienie do swojej odpowiedzi lub małe wyjaśnienie? Zobacz tę stronę, aby uzyskać inne pomysły na rozszerzenie odpowiedzi.
Celeo
0

Tego używam na co dzień:

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

Działa to dobrze w systemach Linux i Solaris aż do wersji 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
fr00tyl00p
źródło
1
Od wersji Bash 2.0+ Pośrednie rozszerzenie jest dostępne. Możesz zastąpić długi i skomplikowany (wraz z eval) test -n "$(eval "echo "\${${1}+x}"")"również ekwiwalentem:[[ ${!1+x} ]]
0

Lubię funkcje pomocnicze, aby ukryć prymitywne szczegóły bash. W takim przypadku powoduje to jeszcze więcej (ukrytego) chamstwa:

# 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} ]
}

Ponieważ po raz pierwszy miałem błędy w tej implementacji (zainspirowane odpowiedziami Jensa i Lionela), wpadłem na inne rozwiązanie:

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

Uważam, że jest bardziej bezpośredni, bardziej wstydliwy i łatwiejszy do zrozumienia / zapamiętania. Przypadek testowy pokazuje, że jest równoważny:

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

Przypadek testowy także pokazuje, że local varma nie deklarują var (o ile nie następuje „=”). Od dłuższego czasu myślałem, że zadeklarowałem zmienne w ten sposób, aby odkryć teraz, że po prostu wyraziłem zamiar ... Chyba nie można tego zrobić.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky pasek: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared pasek: 0
IsDeclared baz: 0

BONUS: przypadek użycia

Najczęściej używam tego testu, aby nadać (i zwrócić) parametry funkcjom w nieco „elegancki” i bezpieczny sposób (prawie przypominający interfejs ...):

#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

Wywołanie ze wszystkimi wymaga zadeklarowanych zmiennych

Witaj świecie!

jeszcze:

Błąd: niezadeklarowana zmienna: outputStr

Daniel S.
źródło
0

Po przejrzeniu wszystkich odpowiedzi działa to również:

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

jeśli nie podasz SOME_VARzamiast tego, co mam $SOME_VAR, ustawi to na pustą wartość; $jest konieczne, aby to zadziałało.

Hatem Jaber
źródło
nie zadziała, gdy masz set -uefekt, który wydrukuje unboud variablebłąd
Michael Heuberger
-1

Aby sprawdzić, czy var jest ustawiony, czy nie

var=""; [[ $var ]] && echo "set" || echo "not set"
koola
źródło
3
Nie powiedzie się, jeśli $varzostanie ustawiony pusty ciąg.
HelloGoodbye
-1

Jeśli chcesz sprawdzić, czy zmienna jest powiązana lub niezwiązana, działa to dobrze, nawet po włączeniu opcji rzeczownika:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi
użytkownik1857592
źródło
Myślę, że masz na myśli set -o nounset, nie set -o noun set. Działa to tylko dla edytowanych zmiennych export. Zmienia również ustawienia w sposób, którego cofnięcie jest niewygodne.
Keith Thompson,
-1

Zawsze używam tego, ponieważ wydaje się łatwy do zrozumienia dla każdego, kto zobaczy kod po raz pierwszy:

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

A jeśli chcesz sprawdzić, czy nie pusty;

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

Otóż ​​to.

mijnnaam
źródło
4
Pytanie dotyczy sposobu sprawdzenia, czy zmienna jest ustawiona, a nie, czy zmienna jest pusta.
HelloGoodbye
Prawdopodobnie musisz użyć [[ ... ]]zamiast [ ... ]. To ostatnie jest w rzeczywistości zewnętrznym poleceniem i nie miałoby żadnego wglądu w to, czy zmienna jest ustawiona, czy nie.
solidsnack