So leiten Sie Eingaben an eine Bash while-Schleife weiter und behalten Variablen nach dem Ende der Schleife bei

87

Bash erlaubt zu verwenden: cat <(echo "$FILECONTENT")

Bash erlaubt auch zu verwenden: while read i; do echo $i; done </etc/passwd

Um die beiden vorherigen zu kombinieren, kann Folgendes verwendet werden: echo $FILECONTENT | while read i; do echo $i; done

Das Problem mit dem letzten ist, dass es eine Sub-Shell erstellt und nach dem Ende der while-Schleife inicht mehr auf die Variable zugegriffen werden kann.

Meine Frage ist:

Wie erreiche while read i; do echo $i; done <(echo "$FILECONTENT")ich so etwas: oder mit anderen Worten: Wie kann ich sicher sein, dass idie while-Schleife überlebt?

Bitte beachten Sie, dass mir das Einfügen der while-Anweisung in bekannt ist {}, dies das Problem jedoch nicht löst (stellen Sie sich vor, Sie möchten die while-Schleife in der Funktion und die Rückgabevariable verwenden i).

Wakan Tanka
quelle
Siehe auch : stackoverflow.com/questions/37229058/… . Erläutert alle Optionen, einschließlich der unten genannten Prozessersetzung lastpipeund ihrer Vor- und Nachteile.
ivan_pozdeev

Antworten:

113

Die korrekte Notation für Process Substitution lautet:

while read i; do echo $i; done < <(echo "$FILECONTENT")

Der letzte iin der Schleife zugewiesene Wert ist dann verfügbar, wenn die Schleife endet. Eine Alternative ist:

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

Die geschweiften Klammern sind eine E / A-Gruppierungsoperation und erstellen selbst keine Unterschale. In diesem Zusammenhang sind sie Teil einer Pipeline und werden daher als Subshell ausgeführt. Dies liegt jedoch an der |, nicht an der { ... }. Sie erwähnen dies in der Frage. AFAIK, Sie können innerhalb dieser innerhalb dieser Funktion eine Rückkehr durchführen.


Bash bietet auch das shopteingebaute und eine seiner vielen Optionen ist:

lastpipe

Wenn festgelegt und die Jobsteuerung nicht aktiv ist, führt die Shell den letzten Befehl einer Pipeline aus, die in der aktuellen Shell-Umgebung nicht im Hintergrund ausgeführt wird.

Wenn Sie also so etwas in einem Skript verwenden, wird das Modifizierte sumnach der Schleife verfügbar:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

Wenn Sie dies in der Befehlszeile tun, wird normalerweise die Jobsteuerung nicht aktiv ausgeführt (dh in der Befehlszeile ist die Jobsteuerung aktiv). Das Testen ohne Verwendung eines Skripts ist fehlgeschlagen.

Wie Gareth Rees in seiner Antwort feststellte , können Sie manchmal auch eine Hier-Zeichenfolge verwenden :

while read i; do echo $i; done <<< "$FILECONTENT"

Dies erfordert nicht shopt; Möglicherweise können Sie einen Prozess damit speichern.

Jonathan Leffler
quelle
Verzeihen Sie meine Unwissenheit. Ich weiß, dass dies die richtige Lösung ist, und ich habe sie als Antwort markiert, damit sie für mich funktioniert. Aber jetzt, wenn ich laufe, wurde while read i; do echo $i; done < <(cat /etc/passwd); echo $idie letzte Zeile nicht zweimal zurückgegeben. Was mache ich falsch?
Wakan Tanka
@WakanTanka: Ich musste ein bisschen experimentieren ... Ich glaube, die Antwort ist, dass der fehlgeschlagene Lesevorgang iauf leer zurückgesetzt wird, sodass das Echo nach der Schleife eine leere Zeile wiedergibt.
Jonathan Leffler
1
Ein großes Lob für die Referenz zur Prozesssubstitution. Ich wusste es nicht.
Atcold
@ Wakan Tanka: habe das gleiche Ergebnis wie deins, ich benutze while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$xfunktioniert, // vielleicht hat sich das Bash-Verhalten in diesen Tagen geändert?
Yurenchen
Du bist ein Lebensretter! Ich habe stundenlang nach einer ähnlichen Lösung gesucht. Ihre ist die einzige, die funktioniert hat.
Pat
0

Diese Funktion dupliziert $ NUM-Zeiten von JPG-Dateien (Bash)

function makeDups() {
NUM=$1
echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}
Steve
quelle