So erkennen Sie, ob in einem Bash-Shell-Skript keine Zeichenfolge definiert ist

151

Wenn ich nach der Nullzeichenfolge suchen möchte, würde ich dies tun

[ -z $mystr ]

aber was ist, wenn ich überprüfen möchte, ob die Variable überhaupt definiert wurde? Oder gibt es keinen Unterschied in Bash-Skripten?

Setjmp
quelle
27
Bitte haben Sie die Angewohnheit, [-z "$ mystr"] im Gegensatz zu [-z $ mystr] zu verwenden
Charles Duffy
Wie macht man das Gegenteil? Ich meine, wenn die Zeichenfolge nicht null ist
Öffnen Sie den Weg
1
@flow: Was ist mit[ -n "${VAR+x}"] && echo not null
Lekensteyn
1
@ CharlesDuffy Ich habe dies bereits in vielen Online-Ressourcen gelesen. Warum ist das bevorzugt?
Ffledgling
6
@Ayos, denn wenn Sie keine Anführungszeichen haben, wird der Inhalt der Variablen in Zeichenfolgen aufgeteilt und globalisiert. Wenn es eine leere Zeichenfolge ist, wird es [ -z ]anstelle von [ -z "" ]; wenn es Leerzeichen hat, wird es [ -z "my" "test" ]statt [ -z my test ]; und wenn ja [ -z * ], wird das *durch die Namen der Dateien in Ihrem Verzeichnis ersetzt.
Charles Duffy

Antworten:

138

Ich denke , die Antwort , die Sie nach sind impliziert (wenn nicht angegeben) von Vinko ‚s Antwort , obwohl es einfach nicht ausgeschrieben ist. Um zu unterscheiden, ob VAR festgelegt, aber leer oder nicht festgelegt ist, können Sie Folgendes verwenden:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Sie können die beiden Tests in der zweiten Zeile wahrscheinlich zu einem kombinieren mit:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Wenn Sie jedoch die Dokumentation zu Autoconf lesen, werden Sie feststellen, dass die Kombination von Begriffen mit ' -a' nicht empfohlen wird und die Verwendung separater einfacher Tests in Kombination mit ' ' empfohlen wird &&. Ich bin nicht auf ein System gestoßen, bei dem ein Problem vorliegt. das bedeutet nicht, dass sie früher nicht existierten (aber sie sind heutzutage wahrscheinlich extrem selten, auch wenn sie in der fernen Vergangenheit nicht so selten waren).

Einzelheiten zu diesen und anderen verwandten Shell-Parametererweiterungen , den Befehlen testoder[ und den bedingten Ausdrücken finden Sie im Bash-Handbuch.


Ich wurde kürzlich per E-Mail nach dieser Antwort mit der Frage gefragt:

Sie verwenden zwei Tests, und ich verstehe den zweiten gut, aber nicht den ersten. Genauer gesagt verstehe ich die Notwendigkeit einer variablen Erweiterung nicht

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

Würde dies nicht dasselbe bewirken?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Faire Frage - die Antwort lautet "Nein, Ihre einfachere Alternative macht nicht dasselbe".

Angenommen, ich schreibe dies vor Ihrem Test:

VAR=

Ihr Test sagt "VAR ist überhaupt nicht gesetzt", aber meiner sagt (implizit, weil es nichts wiedergibt) "VAR ist gesetzt, aber sein Wert ist möglicherweise leer". Versuchen Sie dieses Skript:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

Die Ausgabe ist:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

Im zweiten Testpaar wird die Variable gesetzt, aber auf den leeren Wert gesetzt. Dies ist die Unterscheidung, die die ${VAR=value}und ${VAR:=value}Notationen machen. Das Gleiche gilt für ${VAR-value}und ${VAR:-value}und ${VAR+value}und ${VAR:+value}und und so weiter.


Wie Gili in seiner Antwort betont, schlägt die obige grundlegende Antwort mit fehl , wenn Sie bashmit der set -o nounsetOption arbeiten unbound variable. Es ist leicht zu beheben:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Oder Sie können die set -o nounsetOption mit abbrechen set +u( set -uentspricht set -o nounset).

Jonathan Leffler
quelle
7
Das einzige Problem, das ich mit dieser Antwort habe, ist, dass sie ihre Aufgabe eher indirekt und unklar erfüllt.
Schweizer
3
Wenn Sie auf der Bash-Manpage nach der Beschreibung der oben genannten Bedeutung suchen möchten, lesen Sie den Abschnitt "Parametererweiterung" und dann den folgenden Text: "Wenn Sie keine Teilstringerweiterung durchführen, verwenden Sie die unten dokumentierten Formulare für Bash-Tests Ein Parameter, der nicht gesetzt oder null ist ['null' bedeutet die leere Zeichenfolge]. Das Weglassen des Doppelpunkts führt nur zu einem Test für einen Parameter, der nicht gesetzt ist (...) $ {parameter: + word}: Verwenden Sie den alternativen Wert. If-Parameter ist null oder nicht gesetzt, nichts wird ersetzt, andernfalls wird die Erweiterung des Wortes ersetzt. "
David Tonhofer
2
@Schweiz Das ist weder unklar noch indirekt, sondern idiomatisch. Vielleicht für einen Programmierer, der nicht vertraut ist ${+}und der ${-}unklar ist, aber die Vertrautheit mit diesen Konstrukten ist wesentlich, wenn man ein kompetenter Benutzer der Shell sein will.
William Pursell
Siehe stackoverflow.com/a/20003892/14731, wenn Sie dies mit set -o nounsetaktiviertem Gerät implementieren möchten .
Gili
Ich habe viele Tests durchgeführt; weil die Ergebnisse in meinen Skripten inkonsistent waren. Ich schlage vor, dass die Leute den " [ -v VAR ]" -Ansatz mit bash -s v4.2 und darüber hinaus untersuchen .
wird
38
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z funktioniert auch für undefinierte Variablen. Um zwischen einem undefinierten und einem definierten zu unterscheiden, verwenden Sie die hier oder mit klareren Erklärungen hier aufgeführten Dinge .

Am saubersten ist die Verwendung der Erweiterung wie in diesen Beispielen. Um alle Optionen zu erhalten, lesen Sie den Abschnitt Parametererweiterung im Handbuch.

Alternatives Wort:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Standardwert:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

Natürlich würden Sie einen dieser Werte anders verwenden, indem Sie den gewünschten Wert anstelle des Standardwerts verwenden und die Erweiterung gegebenenfalls direkt verwenden.

Vinko Vrsalovic
quelle
1
Aber ich möchte unterscheiden, ob die Zeichenfolge "" ist oder noch nie definiert wurde. Ist das möglich?
Setjmp
1
Diese Antwort zeigt , wie man zwischen diesen beiden Fällen unterscheidet. Folgen Sie dem Link zur Bash-FAQ, um weitere Informationen zu erhalten.
Charles Duffy
1
Ich fügte hinzu, dass nach seinem Kommentar Charles
Vinko Vrsalovic
2
Suchen Sie in der Bash-Manpage nach "Parametererweiterung" für all diese "Tricks". ZB $ {foo: -default}, um einen Standardwert zu verwenden, $ {foo: = default}, um den Standardwert zuzuweisen, $ {foo :? Fehlermeldung}, um eine Fehlermeldung anzuzeigen, wenn foo nicht gesetzt ist usw.
Jouni K. Seppänen
18

Advanced Bash Scripting Guide, 10.2. Parameterersetzung:

  • $ {var + blahblah}: Wenn var definiert ist, wird der Ausdruck durch 'blahblah' ersetzt, andernfalls wird null ersetzt
  • $ {var-blahblah}: Wenn var definiert ist, wird es selbst ersetzt, andernfalls wird 'blahblah' ersetzt
  • $ {var? blahblah}: Wenn var definiert ist, wird es ersetzt, andernfalls existiert die Funktion mit 'blahblah' als Fehlermeldung.


Um Ihre Programmlogik darauf zu stützen, ob die Variable $ mystr definiert ist oder nicht, können Sie Folgendes tun:

isdefined=0
${mystr+ export isdefined=1}

Wenn nun isdefined = 0 ist, war die Variable undefiniert. Wenn isdefined = 1 ist, wurde die Variable definiert

Diese Art der Überprüfung von Variablen ist besser als die obige Antwort, da sie eleganter und lesbarer ist. Wenn Ihre Bash-Shell so konfiguriert wurde, dass bei der Verwendung undefinierter Variablen Fehler auftreten (set -u), wird das Skript vorzeitig beendet.


Andere nützliche Dinge:

Um $ mystr einen Standardwert von 7 zuzuweisen, wenn er nicht definiert war, und ihn ansonsten intakt zu lassen:

mystr=${mystr- 7}

So drucken Sie eine Fehlermeldung und beenden die Funktion, wenn die Variable undefiniert ist:

: ${mystr? not defined}

Beachten Sie hier, dass ich ':' verwendet habe, um den Inhalt von $ mystr nicht als Befehl auszuführen, falls er definiert ist.


quelle
Ich wusste nichts über das? Syntax für BASH-Variablen. Das ist ein schöner Fang.
Schweizer
Dies funktioniert auch mit indirekten Variablenreferenzen. Dies geht vorbei: var= ; varname=var ; : ${!varname?not defined}und dies endet : varname=var ; : ${!varname?not defined}. Aber es ist eine gute Angewohnheit, set -udie das Gleiche viel einfacher macht.
Ceving
11

Eine Zusammenfassung der Tests.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
k107
quelle
Gute Praxis in Bash. Manchmal haben Dateipfade Leerzeichen oder zum Schutz vor Befehlsinjektion
k107
1
Ich denke , mit ${var+x}ihm nicht notwendig , außer, wenn variable Namen dürfen in ihnen Räume haben.
Anne van Rossum
Nicht nur gute Praxis; es ist erforderlich , mit [richtigen Ergebnisse zu erzielen. Wenn varnicht gesetzt oder leer, [ -n $var ]reduziert sich auf [ -n ]den Exit-Status 0, während [ -n "$var" ]der erwartete (und korrekte) Exit-Status von 1 hat.
chepner
5

https://stackoverflow.com/a/9824943/14731 enthält eine bessere Antwort (eine, die besser lesbar ist und mit set -o nounsetaktiviert funktioniert ). Es funktioniert ungefähr so:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi
Gili
quelle
4

Die explizite Möglichkeit, nach einer zu definierenden Variablen zu suchen, wäre:

[ -v mystr ]
Felix Leipold
quelle
1
Sie können dies in keiner der Manpages finden (für Bash oder Test), aber es funktioniert für mich. Haben Sie eine Idee, welche Versionen dies hinzugefügt wurde?
Ash Berlin-Taylor
1
Es ist in Bedingten Ausdrücken dokumentiert , auf die der testBediener am Ende des Abschnitts Bourne Shell Builtins verweist . Ich weiß nicht, wann es hinzugefügt wurde, aber es ist nicht in der Apple-Version von bash(basierend auf bash3.2.51) enthalten, daher handelt es sich wahrscheinlich um eine 4.x-Funktion.
Jonathan Leffler
1
Das funktioniert bei mir nicht GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu). Der Fehler ist -bash: [: -v: unary operator expected. Laut einem Kommentar hier ist mindestens Bash 4.2 erforderlich, um zu funktionieren.
Acumenus
Vielen Dank für die Überprüfung, wann es verfügbar wurde. Ich hatte es schon einmal auf verschiedenen Systemen verwendet, aber dann funktionierte es plötzlich nicht mehr. Ich bin aus Neugier hierher gekommen und ja, natürlich ist die Bash auf diesem System eine 3.x Bash.
Clacke
1

eine weitere Option: die Erweiterung "Listenarray-Indizes" :

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

Das einzige Mal, wenn dies auf die leere Zeichenfolge erweitert wird, ist, wenn fooes nicht festgelegt ist. Sie können es also mit der Zeichenfolgenbedingung überprüfen:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

sollte in jeder Bash-Version> = 3.0 verfügbar sein

Aaron Davies
quelle
1

dieses Fahrrad nicht noch weiter zu vergießen, wollte aber hinzufügen

shopt -s -o nounset

ist etwas, das Sie am Anfang eines Skripts hinzufügen können. Dieser Fehler tritt auf, wenn Variablen an keiner Stelle im Skript deklariert werden. Die Nachricht, die Sie sehen würden, ist unbound variable, aber wie andere erwähnen, wird keine leere Zeichenfolge oder kein Nullwert abgefangen. Um sicherzustellen, dass ein einzelner Wert nicht leer ist, können wir eine Variable testen, mit der sie erweitert wird ${mystr:?}, auch als Dollarzeichenerweiterung bezeichnet, was zu Fehlern führen würde parameter null or not set.

Aufopferungsvoll
quelle
1

Das Bash-Referenzhandbuch ist eine maßgebliche Informationsquelle zu Bash.

Hier ist ein Beispiel für das Testen einer Variablen, um festzustellen, ob sie vorhanden ist:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

(Aus Abschnitt 6.3.2 .)

Beachten Sie, dass das Leerzeichen nach dem offenen [und bevor das ]ist nicht optional .


Tipps für Vim-Benutzer

Ich hatte ein Skript mit mehreren Deklarationen wie folgt:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Aber ich wollte, dass sie sich auf bestehende Werte beschränken. Also habe ich sie neu geschrieben, um so auszusehen:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

Ich konnte dies in vim mithilfe eines schnellen regulären Ausdrucks automatisieren:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Dies kann angewendet werden, indem die relevanten Zeilen visuell ausgewählt und dann eingegeben werden :. Die Befehlsleiste wird mit gefüllt :'<,'>. Fügen Sie den obigen Befehl ein und drücken Sie die Eingabetaste.


Getestet auf dieser Version von Vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

Windows-Benutzer möchten möglicherweise unterschiedliche Zeilenenden.

Funroll
quelle
0

Ich denke, dies ist eine viel klarere Möglichkeit, um zu überprüfen, ob eine Variable definiert ist:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Verwenden Sie es wie folgt:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi
schweizerisch
quelle
1
Eine Funktion zu schreiben ist keine schlechte Idee; anrufen grepist schrecklich. Beachten Sie, dass bashStützen ${!var_name}als Weg , um den Wert einer Variablen des Erhaltens , dessen Name in angegeben $var_name, so name=value; value=1; echo ${name} ${!name}Ausbeuten value 1.
Jonathan Leffler
0

Eine kürzere Version zum Testen undefinierter Variablen kann einfach sein:

test -z ${mystr} && echo "mystr is not defined"
nfleury
quelle
-3

call set ohne Argumente. Es gibt alle definierten Variablen aus.
Die letzten in der Liste sind die in Ihrem Skript definierten.
Sie können also die Ausgabe an etwas weiterleiten, das herausfinden kann, welche Dinge definiert sind und welche nicht

Aditya Mukherji
quelle
2
Ich habe das nicht herabgestimmt, aber es ist so etwas wie ein Vorschlaghammer, eine ziemlich kleine Nuss zu knacken.
Jonathan Leffler
Diese Antwort gefällt mir tatsächlich besser als die akzeptierte Antwort. Es ist klar, was in dieser Antwort getan wird. Die akzeptierte Antwort ist höllisch wackelig.
Schweizer
Es scheint, dass diese Antwort eher ein Nebeneffekt der Implementierung von "set" ist als etwas, das wünschenswert ist.
Jonathan Morgan