temporärer Wert des Bash-Skripts auf Befehl

11

Wie unten Befehl,

if true; then
   IFS=":" read a b c d e f <<< "$test"

Das Buch sagt, wenn der Wertzuweisungsbefehl ( IFS ":") vor dem Hauptbefehl ( read a b c d e f <<< "$value") verwendet wird, wirkt sich sein Wert vorübergehend auf den Hauptbefehl aus. Der readBefehl verwendet also ein Trennzeichen :.

Aber wie dieser Befehl,

if true; then
   HOME="hello" echo "$HOME"

Echo-Nachricht ist nicht hallo. Was ist die wahre Bedeutung des obigen Befehls?

A.Cho
quelle

Antworten:

5

Dies hängt von der Frage ab, wie die Bewertung funktioniert. Beide Beispiele funktionieren auf die gleiche Weise. Das Problem tritt auf, weil die Shell (hier bash) Variablen erweitert.

Wenn Sie diesen Befehl schreiben:

HOME="foo" echo $HOME

Das $HOMEwird erweitert, bevor der Befehl ausgeführt wird . Daher wird es auf den ursprünglichen Wert erweitert und nicht auf den neuen, den Sie für den Befehl festgelegt haben. Die HOMEVariable wurde in der Umgebung, in der der echoBefehl ausgeführt wird, tatsächlich geändert. Sie drucken sie jedoch $HOMEvom übergeordneten Element aus.

Betrachten Sie zur Veranschaulichung Folgendes:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Wie Sie oben sehen können, gibt der erste Befehl den vorübergehend geänderten Wert von HOMEund der zweite das Original aus. Dies zeigt, dass die Variable nur vorübergehend geändert wurde. Da der bash -c ...Befehl in einfache Anführungszeichen ( ' ') anstatt in doppelte Anführungszeichen ( ) eingeschlossen ist " ", wird die Variable nicht erweitert und unverändert an den neuen Bash-Prozess übergeben. Dieser neue Prozess erweitert es dann und druckt den neuen Wert, auf den er eingestellt wurde. Sie können dies sehen, wenn Sie Folgendes verwenden set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Wie Sie oben sehen können, wird die Variable $HOME niemals an übergeben echo. Es sieht nur seinen erweiterten Wert. Vergleichen mit:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Hier werden aufgrund der einfachen Anführungszeichen die Variable und nicht ihr Wert an den neuen Prozess übergeben.

terdon
quelle
7

Wenn die Shell eine Zeile analysiert, wird die Zeile in Wörter unterteilt, verschiedene Erweiterungen (in der angegebenen Reihenfolge) für die Wörter ausgeführt und anschließend der Befehl ausgeführt.

Annehmen test=1:2:3:4:5:6

Schauen wir uns diesen Befehl an: IFS=":" read a b c d e f <<< "$test"

Nach dem Tokenisieren und der Parametererweiterung :IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

Die Shell legt die IFS-Variable für die Dauer des Lesebefehls fest und readweiß, wie $ IFS auf ihre Eingabe angewendet wird , und gibt den Variablennamen Werte.

Dieser Befehl hat eine ähnliche Geschichte, aber ein anderes Ergebnis: HOME="hello" echo "$HOME"

Da die Parametererweiterung vor Beginn des Befehls erfolgt, hat die Shell:

HOME="hello" echo "/home/username"

Und dann wird während der Ausführung des Echo-Befehls der neue Wert von $ HOME überhaupt nicht verwendet.

Wählen Sie eine der Optionen aus, um das zu erreichen, was Sie versuchen

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

oder

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

aber wähle nicht den ersten aus.

Glenn Jackman
quelle
Es ist wahrscheinlich besser, den ersten auszuwählen. Zumindest ist es deutlich schneller. Wenn eval die Antwort ist, stellen Sie manchmal wahrscheinlich die falsche Frage. Aber wenn jemand dies aus bestimmten Gründen tun muss, macht das Ändern der Antwort die Frage selbst nicht weniger falsch. Eine andere Lösung besteht darin, es in eine Funktion zu verpacken und zu verwenden local.
user23013
Warum sollte die evalLösung vermieden werden?
DarkHeart
Wenn Sie die Eingabe nicht streng kontrollieren, müssen Sie sehr vorsichtig mit dem Code sein, den andere Personen in Ihr Programm einfügen dürfen.
Glenn Jackman
-1

Es gibt zwei Bereiche: Umgebungsvariablen und lokale Variablen. Umgebungsvariablen sind für jeden Prozess gültig (siehe setenv, getenv), während die lokalen Variablen nur innerhalb dieser Shell-Sitzung aktiv sind. (Es ist kein offensichtlicher Unterschied ...)

Impliziert env(wie in Ihrem Beispiel) ändert die Umgebung, während echo ...die lokalen verwendet werden - envhat also keine Auswirkung.

Verwenden Sie zum Ändern der lokalen Variablen beispielsweise

( HOME="foo" ; echo "$HOME" )

Hier definieren die Klammern den Umfang dieser Zuordnung.

Andrew Miloradovsky
quelle
1
Dies hat nichts mit dem Variablenbereich zu tun. Das Problem besteht darin, dass die Variable erweitert wird, bevor sie an die untergeordnete Shell übergeben wird.
Terdon