Wie überprüfe ich, ob eine Variable in einer if-Anweisung vorhanden ist?

69

Ich muss die Existenz einer Variablen in einer ifAnweisung überprüfen . Etwas zur Wirkung von:

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

Und die naheliegendste Frage war diese , die meine Frage nicht wirklich beantwortet.

Interessant...
quelle
Wenn Sie festlegen möchten , $somevarauf einen Wert / string , wenn die Variable existiert nicht: ${somevar:=42}.
Cyrus
Persönlich neige ich dazu, nur auf Leere ( [ -n "$var" ]oder [ ! -z "$var" ]) zu prüfen . Ich finde die Existenz- / Nichtexistenzprüfungen zu subtil und bevorzuge meinen Code grob und einfach.
PSkocik
Warum brauchst du das vs [ -n "$var" ]? Related: stackoverflow.com/questions/3601515/…
Ciro Santilli am

Antworten:

97

In der modernen Bash (Version 4.2 und höher):

[[ -v name_of_var ]]

Von help test:

-v VAR, True, wenn die Shell-Variable VAR gesetzt ist

Chris Down
quelle
3
Funktioniert auch mit einzelnen Klammern: [ -v name_of_var ].
Meuh
7
Beachten Sie, dass für Hashes und Arrays false zurückgegeben wird, sofern die Variable nicht das Element key / indice "0" enthält. Bei namerefs wird geprüft, ob das Ziel definiert ist. Es ist nicht für spezielle Parameter funktionieren wie $1, $-, $#...
Stéphane Chazelas
4
Diese Funktion ist nur in der Bash testoder eingebaut [. Es ist nicht verfügbar in /usr/bin/test. Vergleichen Sie man testmit help test.
Mark Lakata
@MarkLakata Richtig, da externe Befehle den internen Status der Shell nicht kennen können.
Chris Down
ähm, sollte es nicht immer [[-v "$ name_of_var"] sein?
Alexander Mills
24

Hängt davon , was Sie mit existiert .

Hat eine Variable, die deklariert wurde , aber nicht zugewiesen existieren ?

Enthält ein Array (oder hash) Variable, die eine leere Liste zugeordnet wurde , existieren ?

Gibt es eine nameref Variable zeigt auf eine Variable, die derzeit nicht zugeordnet existieren ?

Sind Sie der Meinung $-, $#, $1Variablen? (POSIX nicht).

In Bourne-ähnlichen Muscheln lautet der kanonische Weg:

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

Das funktioniert für skalare Variablen und andere Parameter zu sagen , ob eine Variable einen Wert zugewiesen wurde (leer oder nicht, automatisch aus der Umgebung, assigments, read, foroder andere).

Bei Shells mit einem typesetoder declare-Befehl werden die deklarierten, aber nicht zugewiesenen Variablen mit Ausnahme von in nicht als gesetzt gemeldet .zsh

Für Shells, die Arrays unterstützen, mit Ausnahme von yashund zsh, die nur dann als Set- Array-Variablen gemeldet werden, wenn das Element von Index 0 festgelegt wurde.

Für bash(aber nicht ksh93noch zsh), für Variablen vom Typ assoziatives Array , die sie nicht als gesetzt melden würden, es sei denn, ihr Element des Schlüssels "0" wurde gesetzt.

Für ksh93und bashfür Variablen vom Typ nameref wird nur true zurückgegeben, wenn die vom nameref referenzierte Variable selbst als gesetzt betrachtet wird .

Für ksh, zshund bashein potenziell besseren Ansatz könnte sein:

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

Für ksh93, zshund bash4.4 oder höher, gibt es auch:

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

Welche Variablen melden, die gesetzt oder deklariert wurden.

Stéphane Chazelas
quelle
1
declare -pIch typeset -parbeite bashjetzt auch mit.
cas
1
@cas, nein. Nicht für deklarierte aber nicht gesetzte Variablen. Versuchen Sie bash -c 'typeset -i a; typeset -p a'und vergleichen Sie mit ksh93oder zsh.
Stéphane Chazelas
+1 Danke, dass du mir hierher weisst. Siehe auch meine Frage unix.stackexchange.com/q/280893/674
Tim
@cas, das hat sich mit bash-4.4 geändert (veröffentlicht im September 2016), das habe ich in geändert.
Stéphane Chazelas
9

Wie in der Antwort zu SO erwähnt , können Sie Folgendes überprüfen:

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

Dabei ist $ {somevar + x} eine Parametererweiterung, die den Wert null annimmt, wenn var nicht gesetzt ist, und andernfalls die Zeichenfolge "x" ersetzt.

Bei Verwendung von wird -n, wie in der anderen Antwort vorgeschlagen, nur geprüft, ob die Variable eine leere Zeichenfolge enthält. Es wird seine Existenz nicht überprüfen.

shivams
quelle
2
Sie müssen zitieren, $somevarum damit umzugehen IFS=x. Entweder das oder Zitat x.
mikeserv
1
@mikeserv Danke, ich lerne gerne über Edge Cases :) Meinst du if [ -z "${somevar+x}" ]? Wäre das Zitat innen [[und außen noch erforderlich ]]?
Tom Hale
@TomHale - ja, in seltenen Fällen. Die [ testRoutinen akzeptieren Befehlszeilenparameter, und daher sollte man sich darauf verlassen, dass die üblichen Erweiterungen und Interpretationen, wie sie in üblicher Weise angeordnet sind, beim Aufruf des angewendeten Tests das wiedergeben, was Sie veranlassen sollten, von einer beliebigen Programmbefehlszeile gelesen zu werden. test {! + "!"}
mikeserv
@mikeserv Ich denke dein Ja ist meine 2. Frage ... Ist das erste auch ein Ja?
Tom Hale
1
+1; Dies scheint der einfachste Weg zu sein, wenn dies set -uin Kraft ist und die Bash-Version vor 4.2 vorliegt.
Kyle Strand
4

POSIXly:

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

oder Sie können Ihre Shell die Nachricht für Sie anzeigen lassen:

(: "${somevar?}")
zsh: somevar: parameter not set
cuonglm
quelle
@mikeserv: Ja, natürlich gibt es viele Möglichkeiten. Erstens glaube ich , dass ich es mit dupliziert wird dies . Aber in dieser Frage wollte das OP nur prüfen, er behauptete nicht, dass er die Variable verlassen oder melden wollte, wenn sie nicht gesetzt war, also kam ich mit einer Check-in-Subshell.
Cuonglm
2
Nun, ich weiß, aber es liegt genau daran, dass Sie es in einer Subshell wie dieser tun müssen, was darauf hinweist, dass es möglicherweise nicht der beste Weg ist, hier zu testen ein Fehler. Portabel kann sogar ein trapEXIT nur funktionieren. Das ist alles, was ich sage - es gilt einfach nicht als bestanden / nicht bestanden sehr gut. Und das rede ich auch nicht - genau das habe ich schon gemacht und es hat einen kleinen Kommentar-Chat gekostet, um mich zu überzeugen. Also dachte ich nur, ich würde es vorwärts bezahlen.
mikeserv
1
@mikeserv: Na ja, das ist ein guter Punkt. Ich frage mich nur, ob das Hinzufügen dazu führen kann, dass das OP mit der Syntax verwechselt wird :)
cuonglm
2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi
Mel
quelle
Ich würde mir vorstellen, dass dies etwas ineffizient ist, aber es ist sehr einfach und shkompatibel, genau das, was ich brauche.
Hoijui
2
printf ${var+'$var exists!\n'}

... druckt überhaupt nichts, wenn dies nicht der Fall ist. Oder...

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

... wird dir so oder so sagen.

Sie können den Rückgabewert eines Tests verwenden, um dynamisch die entsprechende Formatzeichenfolge für Ihre Bedingung zu erweitern:

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

Sie können einen printfFehler auch aufgrund einer Substitution machen ...

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

... der $var does not exist!nach stderr druckt und einen anderen Wert als 0 zurückgibt, wenn er nicht gesetzt $varist, aber $var does exist!nach stdout druckt und 0 zurückgibt, wenn er $vargesetzt ist.

mikeserv
quelle
1

Diese einfache Linie funktioniert (und funktioniert auf den meisten POSIX-Shells):

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

Oder in einer längeren Form geschrieben:

unset var

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

Die Erweiterung ist:

  • Wenn die Variable einen Wert (gerade Null) hat, wird false ersetzt
  • Wenn die Variable "kein Wert" hat, wird "kein Wert" (null) ersetzt.

Die ${var+"false"}Erweiterung wird entweder auf "null" oder auf "false" erweitert.
Dann wird "nichts" oder "falsch" ausgeführt und der Beendigungscode gesetzt.

Es ist nicht erforderlich, den Befehl test( [oder [[) aufzurufen, da der Exit-Wert durch die (Ausführung) der Erweiterung selbst festgelegt wird.


quelle
Ja, außer in einigen alten Versionen von zsh in sh emulation, wenn $IFSf, a, l, s oder e enthalten sind. Wie bei anderen Antworten gibt es den Fall von Arrays, Hashes oder anderen Variablentypen, die Sie vielleicht erwähnen möchten.
Stéphane Chazelas
@ StéphaneChazelas schrieb ich most POSIX shells. most bedeutetIn the greatest number of instances , nicht alle. ... ... Also ja, in einem obskuren Zustand when $IFS contains f, a, l, s or eund für eine obskure Muschel some old versions of zshschlägt dies fehl: Was für ein Schock! Ich sollte davon ausgehen, dass ein solcher Fehler längst behoben ist. ... ... Schlagen Sie vor, dass wir Code für vor langer Zeit zerbrochene Muscheln schreiben müssen?
@ StéphaneChazelas Auch: Der Fragentitel ist sehr spezifisch: Bash.
binäres Zebra ???
mikeserv
0

Die reine Muschelweise:

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

Testskript:

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

Ergebnisse:

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
Andreas Mikael Bank
quelle
1
Schlägt fehl, wenn die Variable auf null gesetzt ist.
Tom Hale
0

Mit der Bash 4.4.19 funktionierte folgendes für mich. Hier ist ein vollständiges Beispiel

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi
Aftab Naveed
quelle
-1

Sie können den ifBefehl nicht verwenden , um die Existenz deklarierter Variablen in bash zu überprüfen. Die -vOption ist jedoch in neueren bash- bashVersionen vorhanden, ist jedoch nicht portierbar und kann in älteren Versionen nicht verwendet werden . Denn wenn Sie eine Variable verwenden, die es nicht gibt, wird sie gleichzeitig geboren.

Stellen Sie sich zum Beispiel vor, ich hätte der MYTESTVariablen keinen Wert zugewiesen , aber wenn Sie den Befehl echo verwenden, wird nichts angezeigt! Oder wenn Sie es verwenden if [ -z $MYTEST ], wurde der Wert Null zurückgegeben! Es wurde kein weiterer Exit-Status zurückgegeben, der besagt, dass diese Variable nicht existiert!

Jetzt haben Sie zwei Lösungen (ohne -vOption):

  1. Mit declareBefehl.
  2. Mit setBefehl.

Zum Beispiel:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Leider zeigt Ihnen dieser Befehl auch die geladenen Funktionen im Speicher! Sie können den declare -p | grep -q MYTEST ; echo $?Befehl für ein saubereres Ergebnis verwenden.

Sepahrad Salour
quelle
-1

Funktion zum Prüfen, ob die Variable deklariert / nicht gesetzt ist

einschließlich leer $array=()


Neben @ Gilles Antwort

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

- was ich nicht einen Weg zu kapseln sie innerhalb einer Funktion gefunden haben - ich möchte eine einfache Version hinzuzufügen, die auf zum Teil basiert Richard Hansen ‚s Antwort , aber tut Adresse auch die Gefahr , die mit einem leeren auftritt 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;} 
}
  • Wenn Sie zuerst testen, ob die Variable (un) gesetzt ist, kann der Aufruf zur Deklaration vermieden werden, falls dies nicht erforderlich ist.
  • Wenn jedoch $1der Name eines Leerzeichens enthalten ist $array=(), stellt der Aufruf zur Deklaration sicher, dass wir das richtige Ergebnis erhalten
  • Es werden nie viele Daten an / dev / null übergeben, da declare nur aufgerufen wird, wenn die Variable nicht gesetzt oder ein leeres Array ist.


Mit folgendem Code können die Funktionen getestet werden:

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

Das Skript sollte zurückkehren

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
Martin Rüegg
quelle
-1

Bash- Funktion, die sowohl für skalare als auch für Array- Typen funktioniert :

Definition

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
}

Aufruf

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
Andrei Pozolotin
quelle