Ist es schädlich, nicht festgelegte Variablen zu verwenden?

Ist es schädlich, nicht festgelegte Variablen zu verwenden?

Angenommen, ich habe den folgenden Code:

# Check if the color prompt is enabled and supported on this system
if [ -n "$force_color_prompt" ] && [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    GREEN="\033[1;32m"
    DIM="\033[2m"
    RESET="\033[00m"
fi

echo -e "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

Wenn die Farbunterstützung aktiviert ist, wird eine schöne, farbige Linie ausgegeben. Wenn die Farbunterstützung nicht aktiviert ist, sind Werte wie ${GREEN}nicht festgelegt und der Text wird im üblichen Weiß mit schwarzem Hintergrund ausgedruckt.

Der Code basiert auf der Tatsache, dass Variablen, die nicht gesetzt sind, einfach als leere Zeichenfolge ausgewertet werden (was bei meinen Tests der Fall ist). Wird dies auf einigen Systemen zu Fehlern oder Problemen führen, oder werden alle nicht vorhandenen Variablenstetsals leere Zeichenfolge auswerten? Gibt es einen Grund, warum ich mich nicht auf diese Mechanik verlassen sollte?

Antwort1

Nicht vorhandene Variablen werden bei einer Erweiterung als $FOOoder (äquivalent) immer als leerer String ausgewertet ${FOO}und es ist nicht schädlich, sich darauf zu verlassen, außer in einem besonderen Fall:

Wenn jemand set -udie aktuelle Shell aufgerufen hat, bevor Sie versucht haben, diese Variable zu verwenden, hat er diese Einstellung aktiviert:

              -u Nicht gesetzte Variablen bei der Ausführung von Parametern als Fehler behandeln.
                      eter-Erweiterung. Wenn die Erweiterung auf einem nicht gesetzten
                      Variable, gibt die Shell eine Fehlermeldung aus, und wenn nicht
                      interaktiv, wird mit einem Status ungleich Null beendet.

Dies bedeutet, dass Sie beim Schreiben einer Funktion, die in ein Skript eingebunden werden soll, das von jemand anderem gesteuert wird, vorsichtig sein müssen, was die Verwendung nicht gesetzter Variablen angeht. Andernfalls set -uwürde das Skript beim ersten Versuch, eine nicht gesetzte Variable zu erweitern, mit einer Fehlermeldung beendet, wenn die betreffende Person diese vor dem Aufruf Ihrer Funktion verwendet hätte.

Wenn Sie Ihr eigenes Skript schreiben, kann es nicht schaden, darauf zu zählen, dass nicht gesetzte Variablen zu einer leeren Zeichenfolge erweitert werden.

BEARBEITEN- Außerdem nur so ein Gedanke: Da Sie das Ganze davon abhängig machen, ob die Farbfunktionen von Terminfo für Ihr Terminal verfügbar sind, warum verwenden Sie dann nicht tatsächlich Terminfo zum Generieren der Sequenzen, anstatt die vt100-Werte fest zu codieren? So etwas wie:

if [ -n "$force_color_prompt" ] && type tput &>/dev/null; then
    GREEN="$(tput setaf 2)$(tput bold)"
    DIM="$(tput dim)"
    RESET="$(tput sgr0)"
fi

Dies kann Ihnen etwas Portabilität zwischen anderen Terminals verschaffen (obwohl zugegebenermaßen die Anzahl der Terminals, die die von Ihnen angezeigten Codes nicht verwenden, klein ist und abnimmt). Es kann auch etwas Portabilität verloren gehen, da einige Funktionen auf einigen Plattformen möglicherweise nicht vorhanden sind, je nachdem, wie korrekt die Terminfo-Definitionen sind. Ihre Abweichung ist nicht genau.

Antwort2

Eines der einzigartigsten Merkmale der POSIX-kompatiblen Shell-Skriptsprache istParametererweiterung. Es kann auf verschiedene Weise verwendet werden, um Aufgaben auszuführen, die normalerweise nicht mit variablen Werten in Verbindung gebracht werden. In der Shell kann eine Variable mehr als nur ein Wert sein – sie kann ein ausführbares Element sein. Sie kann sich selbst testen. Und das geschieht explizit – ohne dass Shell-Optionen festgelegt werden müssen.

Ihr Code könnte beispielsweise so aussehen:

N= ERR='error encountered - exiting' 
: ${force_color_prompt?"$ERR"}
/usr/bin/tput setaf >/dev/null 2>&1 || ${N:?"$ERR"}
: "${GREEN:=\033[1;32m}" "${DIM:=\033[2m}" "${RESET:=\033[00m}"
printf %b\\n \
    "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

Die $NVariable wird explizit auf die Nullzeichenfolge gesetzt. Wenn sie also mit der ${N:?}Form einer Parametererweiterung ausgewertet wird, wird ihre übergeordnete Shell automatisch beendet und die folgende Anweisung ?wird ebenfalls auf Erweiterung ausgewertet. Die Ergebnisse werden auf ausgegeben stderr. Dasselbe gilt für $force_color_prompt-. Wenn sie nicht gesetzt ist, wird das Skript mit einem Fehler beendet und gibt automatisch $ERRauf - all aus.stderr

Die $GREEN $RESETund werden auf die von Ihnen definierten Werte gesetzt, wenn sie entweder aktuell nicht gesetzt sind oder auf die Nullzeichenfolge $DIMgesetzt sind . Dadurch können Sie ihre Werte als Umgebungsvariablen an das Skript übergeben. Wenn sich beispielsweise der obige Ausschnitt in einem Skript mit dem Namen befände und ich es folgendermaßen aufgerufen hätte:''greenworld.sh

GREEN="$(tput setaf 2)$(tput bold)" greenworld.sh

Dann $GREENwürde der Inhalt des Skripts nicht zurückgesetzt, sondern würde stattdessen den expliziten Wert übernehmen, den ich dafür festgelegt habe. Dies macht Shell-Skripte flexibel.

Und tputich persönlich unterstütze die Empfehlung dieser Verwendung, die godlygeek empfohlen hat.

In der Shell kann eine nicht gesetzte Variable manchmal genauso nützlich sein wie eine gesetzte. Hier ist ein anderes Beispiel:

set -- * 
while ${1+:} false ; do
    #do stuff until all positionals are shifted away
shift ; done

In diesem Beispiel wird der erste Parameter, solange er definiert ist, auf das eingebaute :Null der Shell erweitert, was den folgenden falseAufruf folglich zu einem No-Op macht. Sobald jedoch alle Positionsparameter shiftentfernt wurden ${1}, wird es nicht mehr auf diese Weise erweitert und falseaufgerufen, und die whileSchleife endet. Sie können unzählige Variationen davon machen.

Antwort3

Im normalen Gebrauch, d. h. wenn Sie nur Variablen erweitern, wird eine nicht gesetzte Variable in allen Shells im Bourne/POSIX-Stil als leer behandelt. Die Ausnahme ist, wenn set -o unsetaka set -uaktiv ist. In diesem Fall wird die Shell einen Fehler ausgeben, wenn Sie versuchen, auf den Wert einer nicht gesetzten Variable zuzugreifen (siehegodlygeeks Antwort).

Es gibt Möglichkeiten zu testen, ob eine Variable nicht gesetzt oder leer ist. Beispielsweise wird die Konstruktion ${foo-bar}auf den Wert von erweitert, foowenn sie gesetzt ist (selbst wenn sie leer ist), und auf, barwenn foosie nicht gesetzt ist;
${foo:-bar}(mit einem zusätzlichen Doppelpunkt) behandelt eine leere Variable, als wäre sie nicht gesetzt. Andere ähnliche Erweiterungskonstruktionen ${foo+bar}, ${foo?bar}, ${foo=bar}verhalten sich ähnlich. Eine leere Variable erscheint neben anderen Unterschieden auch in der Ausgabe von setund export(falls exportiert). Sie werden nur auf diese Unterschiede stoßen, wenn Sie möchten.

Es gibt jedoch einen anderen Grund,Variablen immer initialisieren: Woher wissen Sie, dass die Variable wirklich nicht gesetzt ist? Der Anrufer hat möglicherweise eine ähnliche Variable für seine eigenen Zwecke definiert. Wenn der Anrufer ein anderer Teil desselben Skripts ist, überschreibt Ihre Verwendung in einer Funktion die des Anrufers, es sei denn, Sie deklarieren die Variable als lokal (was nur in ksh/bash/zsh möglich ist), sodass Variablennamenkonflikte ohnehin ein Problem darstellen. Es kann aber auch vorkommen, dass die Variable in der Umgebung vorhanden ist, weil jemand anderes denselben Variablennamen wie Sie gewählt hat. Es gibt eine Art Konvention, Kleinbuchstaben für Shellvariablen und Großbuchstaben für Umgebungsvariablen zu verwenden, aber das löst nicht alle Konflikte und wird nicht allgemein befolgt.

$ export DIM=1 SUM=42
$ bash
Oh, grüne Welt, ich lass mich jetzt nicht im Stich ...

verwandte Informationen