Testen Sie, ob mehrere Variablen festgelegt sind

19

Ich möchte sicherstellen, dass an einem bestimmten Punkt eines Skripts nach dem sourceEinrichten einer Konfigurationsdatei mehrere Variablen festgelegt werden und, falls dies nicht der Fall ist, die Ausführung abgebrochen wird, um dem Benutzer die fehlende Variable mitzuteilen. Ich habe versucht

for var in $one $two $three ; do
    ...

wenn aber zum Beispiel $twonicht gesetzt ist, wird die Schleife nie ausgeführt $two. Das nächste, was ich versuchte, war

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Aber wenn zwei nicht gesetzt ist, bekomme ich immer noch "zwei ist gesetzt" anstelle von "zwei ist nicht gesetzt".

Wie kann ich sicherstellen, dass alle erforderlichen Variablen gesetzt sind?

Update / Lösung: Ich weiß, dass es einen Unterschied zwischen "gesetzt" und "gesetzt, aber leer" gibt. Ich verwende jetzt (danke an /programming//a/16753536/3456281 und die Antworten auf diese Frage) das Folgende:

if [ -n "${!var:-}" ] ; then

Wenn varalso gesetzt, aber leer ist, wird es immer noch als ungültig betrachtet.

Jaspis
quelle
1
Sie können set -udas Skript auch am Anfang anfügen , um es sofort zu beenden, wenn eine nicht festgelegte Variable verwendet wird.
25.

Antworten:

8

Zitierfehler.

if [ -n "${!var}" ] ; then

Für die Zukunft: Setting

set -x

vor dem Ausführen des Codes hätte Ihnen das Problem gezeigt. Anstatt dies dem Code hinzuzufügen, können Sie Ihr Skript mit aufrufen

bash -vx ./my/script.sh
Hauke ​​Laging
quelle
Das funktioniert, aber was passiert mit / ohne Anführungszeichen? Ist meine allgemeine Herangehensweise an erster Stelle richtig?
Jasper
Ja, das ist vorhersehbar: Ich habe die Frage beantwortet, bevor sie gestellt wurde ... 8-)
Hauke ​​Laging
4
@Jasper Sie sollten Variablen immer in Anführungszeichen setzen. Das kostet nicht mehr Zeit als jedes Mal darüber nachzudenken, ob dies notwendig ist. Aber es vermeidet die Fehler.
Hauke ​​Laging
Zitate schützen im Gegenteil nur vor Expansion. Wenn Sie an der Erweiterung interessiert sind, ist das Zitieren sicherlich nicht der richtige Weg. Zum Beispiel: cs = 673.290.765; set - $ (IFS =,; echo $ cs); echo $ #; Ausgabe: 3
mikeserv
2
@mikeserv Nur einfache Anführungszeichen schützen vor Expansion, was ich offensichtlich nicht vorschlage. Doppelte Anführungszeichen schützen vor Worttrennung. Ich habe nicht geleugnet, dass es Fälle geben kann, in denen das Zitieren weggelassen werden muss. Aber das ist kein Argument gegen meine allgemeine Regel. Was für Leute werden so etwas gebrauchen set -- $(IFS=, ; echo $cs)? Die Art von Leuten, die hier fragen müssen, warum if [ -n ${!var} ] ; thennicht funktioniert? Wahrscheinlich nicht.
Hauke ​​Laging
5

Das Einzige, was Sie brauchen, sind Zitate in Ihrem Test:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Funktioniert bei mir.

TNW
quelle
4

Wenn Sie möchten, dass das Programm gestoppt wird:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

Das wird den Trick machen. Jede Nachricht, die auf das Fragezeichen folgt, wird gedruckt stderrund die übergeordnete Shell stirbt ab. OK, also nicht jede Nachricht - nur eine - nur die erste, die fehlschlägt, gibt eine Nachricht aus, da die Shell abstirbt. Ich benutze diese Tests gerne so:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

Ich hatte viel mehr dazu sagen hier .

mikeserv
quelle
2

Du kannst hinzufügen

set -u

an den Anfang Ihres Skripts, um es zu beenden, wenn es versucht, eine nicht festgelegte Variable zu verwenden.

Ein Skript wie

#!/bin/sh
set -u
echo $foo

wird darin enden, dass

script.sh: 3: script.sh: foo: Parameter nicht gesetzt

Wenn Sie bashstattdessen verwenden, sieht der Fehler folgendermaßen aus:

script.sh: Zeile 3: foo: ungebundene Variable

n.st
quelle
Und was macht das Ihrer Meinung nach für Laufzeitprüfungen Sinn?
Hauke ​​Laging
2
@HaukeLaging Ich folge Ihnen nicht ganz - set -uverhindert genau die Art von Fehler, die das OP zu vermeiden versucht, und ist (im Gegensatz zu allen anderen Lösungen) nicht auf einen bestimmten Satz von Variablen beschränkt. Es ist in der Tat eine nützliche Vorsichtsmaßnahme, dass fast alle Shell-Skripte sicher versagen, anstatt unerwartete Dinge zu tun, wenn eine Variable nicht gesetzt ist. Dieser Artikel ist eine praktische Referenz zum Thema.
25.
@ n.st Ich bin anderer Meinung - null kann ein ebenso nützlicher Wert sein wie nicht null, wenn Sie es vorhaben.
mikeserv
1
@ n.st Das macht für das OP keinen Sinn, da er keinen Schutz vor dem Zugriff auf nicht gesetzte Variablen wünscht (Übrigens: Eine gesetzte, aber leere Variable würde den gleichen Fehler verursachen, aber nicht auf Ihren "Schutz" reagieren). Er möchte zur Laufzeit prüfen, ob eine Variable nicht gesetzt / leer ist. Ihr Vorschlag kann als allgemeine Entwicklungshilfe nützlich sein, löst jedoch nicht das Problem des OP.
Hauke ​​Laging
set -uIch denke, es könnte auch verwendet werden, aber es ist nicht so flexibel wie die richtige Parametererweiterung (siehe meine aktualisierte Frage). Und mit set -umüsste ich einen Dummy-Zugriff auf alle Zielvariablen machen, wenn ich ihr Vorhandensein an einem Ort überprüfen möchte.
Jasper
2

Eine maximal benutzerfreundliche Lösung testet alle Anforderungen und meldet sie zusammen, anstatt bei der ersten fehlzuschlagen und ein Hin und Her zu erfordern, um die Dinge richtig zu machen:

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

Ich verwende eine Array-Variable, um zu verfolgen, welche Variablen nicht festgelegt wurden, und benutze das Ergebnis, um eine benutzerbezogene Nachricht zu verfassen.

Anmerkungen:

  • Ich zitierte ${required_vars[@]}in der forSchleife hauptsächlich aus Gewohnheit - ich würde nicht raten, irgendwelche Shell-Metazeichen in Ihre Variablennamen aufzunehmen!
  • Ich habe nicht zitiert ${#missing_vars[@]}, weil das immer eine ganze Zahl ist, auch wenn Sie pervers genug sind, um den vorhergehenden Rat zu ignorieren.
  • Ich habe %qbeim Drucken verwendet; %swürde normalerweise ausreichen.
  • Die Fehlerausgabe wird immer an den Fehlerstrom mit weitergeleitet >&2, sodass sie nicht an nachfolgende Befehle weitergeleitet wird
  • Stille ist Gold wert - drucken Sie keine Fortschrittsinformationen oder Debugging-Informationen aus, es sei denn, Sie werden ausdrücklich dazu aufgefordert. Das macht Fehler offensichtlicher.
Toby Speight
quelle
1

bashMit 4.2 können Sie testen, ob eine Variable mit dem -vOperator festgelegt wurde. Eine nicht festgelegte Variable und eine auf die leere Zeichenfolge festgelegte Variable sind zwei verschiedene Bedingungen:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
chepner
quelle
Ich habe nach "mehreren" Variablen gefragt, also habe ich nach einer Art Indirektion gesucht ...
Jasper
Sie brauchen indirection nicht, da -vist der Name einer Variablen, so for var in one two three; [[ -v $var ]] && ...; donewürde , wenn jeder überprüfen von one, twound threewerden in der Reihenfolge festgelegt.
Chepner
1

Ich denke, wenn Sie meinen not set, so darf die Variable niemals initialisiert werden. Wenn Sie verwenden [ -n "${!var}" ], so wird die leere Variable wie two=""fehlgeschlagen, während es gesetzt ist . Sie können dies versuchen:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done
cuonglm
quelle
0

Eine prägnante (wenn auch etwas hackige) Lösung für den Fall, dass das sourced-Skript die Variablen auf einen Nullwert setzen kann, besteht darin, die Variablen vor dem Aufrufen des Skripts auf einen bestimmten Wert zu initialisieren und anschließend nach diesem Wert zu suchen , z.B

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi
Laurence Renshaw
quelle
1
eine andere schöne Lösung, aber ich möchte eine spezifischere Fehlermeldung als "eine oder mehrere Variablen nicht gesetzt" geben ...
Jasper
0

Ich habe die Antwort von @ mikeserv erweitert .

Diese Version verwendet eine Liste von Variablen, um das Vorhandensein zu überprüfen, den Namen der fehlenden Variablen auszudrucken und im Fehlerfall einen Aufruf von usage () auszulösen.

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Beispielausgabe bei fehlendem Wert:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

Beachten Sie, dass Sie die Variable VALUE in einen alternativen Namen ändern können, der besser zu Ihrer gewünschten Ausgabe passt.

Ryan
quelle