Wenn ich im folgenden Programm die Variable $foo
in der ersten if
Anweisung auf den Wert 1 setze , funktioniert dies in dem Sinne, dass ihr Wert nach der if-Anweisung gespeichert wird. Wenn ich jedoch dieselbe Variable if
innerhalb einer while
Anweisung auf den Wert 2 setze , wird sie nach der while
Schleife vergessen . Es verhält sich so, als würde ich eine Kopie der Variablen $foo
in der while
Schleife verwenden und nur diese bestimmte Kopie ändern. Hier ist ein vollständiges Testprogramm:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Eric Lilja
quelle
quelle
SC2030: Modification of foo is local (to subshell caused by pipeline).
Antworten:
Die
while
Schleife wird in einer Unterschale ausgeführt. Änderungen, die Sie an der Variablen vornehmen, sind nach dem Beenden der Subshell nicht mehr verfügbar.Stattdessen können Sie eine here-Zeichenfolge verwenden , um die while-Schleife neu zu schreiben, damit sie sich im Haupt-Shell-Prozess befindet. wird nur
echo -e $lines
in einer Subshell ausgeführt:Sie können das ziemlich Hässliche
echo
in der obigen Zeichenfolge loswerden, indem Sie die Backslash-Sequenzen sofort beim Zuweisen erweiternlines
. Dort kann die$'...'
Form des Zitierens verwendet werden:quelle
<<< "$(echo -e "$lines")"
zu einfach wechseln<<< "$lines"
tail -f
anstelle von festem Text stammt?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(offensichtlich wird die Schleife nicht beendet, da sie weiterhin auf die Eingabe von wartettail
).while
Schleife oderfor
Schleife zusammen. Vielmehr ist die Verwendung von Subshell, dh incmd1 | cmd2
,cmd2
in einer Subshell. Wenn also einefor
Schleife in einer Subshell ausgeführt wird, wird das unerwartete / problematische Verhalten angezeigt.AKTUALISIERT # 2
Die Erklärung findet sich in der Antwort von Blue Moons.
Alternativlösungen:
Beseitigen
echo
Fügen Sie das Echo im Here-is-the-Document hinzu
Führen Sie
echo
im Hintergrund:Umleiten zu einem Dateihandle explizit (Beachten Sie den Platz in
< <
!):Oder leiten Sie einfach weiter zu
stdin
:Und eine für
chepner
(eliminierenecho
):Eine Variable
$lines
kann in ein Array konvertiert werden, ohne eine neue Sub-Shell zu starten. Die Zeichen\
undn
müssen in ein Zeichen konvertiert werden (z. B. ein echtes neues Zeilenzeichen) und die Variable IFS (Internal Field Separator) verwenden, um die Zeichenfolge in Array-Elemente aufzuteilen. Dies kann wie folgt erfolgen:Ergebnis ist
quelle
lines
einzige Zweck der Variablen darin zu bestehen scheint, diewhile
Schleife zu speisen .for line in $(echo -e $lines); do ... done
Sie sind der 742342. Benutzer, der diese Bash-FAQ fragt . Die Antwort beschreibt auch den allgemeinen Fall von Variablen, die in von Pipes erstellten Unterschalen festgelegt sind:
quelle
Hmmm ... Ich würde fast schwören, dass dies für die ursprüngliche Bourne-Shell funktioniert hat, aber ich habe gerade keinen Zugriff auf eine laufende Kopie, um dies zu überprüfen.
Es gibt jedoch eine sehr triviale Problemumgehung für das Problem.
Ändern Sie die erste Zeile des Skripts von:
zu
Et voila! Ein Lesevorgang am Ende einer Pipeline funktioniert einwandfrei, vorausgesetzt, Sie haben die Korn-Shell installiert.
quelle
Wie wäre es mit einer sehr einfachen Methode
quelle
Dies ist eine interessante Frage und berührt ein sehr grundlegendes Konzept in Bourne Shell und Subshell. Hier biete ich eine Lösung an, die sich von den vorherigen Lösungen durch eine Art Filterung unterscheidet. Ich werde ein Beispiel geben, das im wirklichen Leben nützlich sein kann. Dies ist ein Fragment zum Überprüfen, ob heruntergeladene Dateien einer bekannten Prüfsumme entsprechen. Die Prüfsummendatei sieht folgendermaßen aus (zeigt nur 3 Zeilen):
Das Shell-Skript:
Das übergeordnete Element und die Unterschale kommunizieren über den Befehl echo. Sie können einfach zu analysierenden Text für die übergeordnete Shell auswählen. Diese Methode bricht nicht Ihre normale Denkweise, nur dass Sie eine Nachbearbeitung durchführen müssen. Sie können dazu grep, sed, awk und mehr verwenden.
quelle