Ich bin an bash
die eingebaute read
Funktion von while-Schleifen gewöhnt , z.
echo "0 1
1 1
1 2
2 3" |\
while read A B; do
echo $A + $B | bc;
done
Ich habe an einem make
Projekt gearbeitet, und es wurde ratsam, Dateien zu teilen und Zwischenergebnisse zu speichern. Infolgedessen zerlege ich oft einzelne Zeilen in Variablen. Während das folgende Beispiel ziemlich gut funktioniert,
head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done
Es ist irgendwie dumm, weil die while-Schleife nie mehr als einmal ausgeführt wird. Aber ohne die while
,
head -n1 somefile | read A B C D E FOO; [... use vars here ...]
Die gelesenen Variablen sind immer leer, wenn ich sie benutze. Ich habe dieses Verhalten nie bemerkt read
, weil ich normalerweise while-Schleifen verwenden würde, um viele ähnliche Zeilen zu verarbeiten. Wie kann ich bash
das read
eingebaute System ohne eine while-Schleife verwenden? Oder gibt es eine andere (oder noch bessere) Möglichkeit, eine einzelne Zeile in mehrere (!) Variablen einzulesen?
Fazit
Die Antworten lehren uns, es ist ein Problem des Scoping. Die Aussage
cmd0; cmd1; cmd2 | cmd3; cmd4
wird so interpretiert, dass die Befehle cmd0
, cmd1
und cmd4
in dem gleichen Umfang ausgeführt werden, während die Befehle cmd2
und cmd3
jeweils ihre eigenen Subshell gegeben und damit verschiedene Bereiche. Die ursprüngliche Shell ist das übergeordnete Element beider Subshells.
… cmd1; | cmd2 …
ist ein Fehler. (2) Wir können das sagencmd2
und bekommencmd3
jeweils ihre eigene Unterschale,cmd1; cmd2 | cmd3; cmd4
indem wir Dinge wieanswer=42 | sleep 0
oder ausprobierencd other/dir | read foo
. Sie werden sehen, dass sich der erste Befehl in jeder Pipeline in einer Subshell befinden muss, da sie den Hauptprozess (übergeordnete Shell) nicht beeinflussen.Antworten:
Dies liegt daran, dass der Teil, in dem Sie die Vars verwenden, ein neuer Befehlssatz ist. Verwenden Sie stattdessen Folgendes:
Beachten Sie, dass in dieser Syntax ein Leerzeichen nach dem
{
und ein;
(Semikolon) vor dem stehen muss}
. Auch-n1
ist nicht notwendig;read
liest nur die erste Zeile.Zum besseren Verständnis kann dies hilfreich sein. es macht das gleiche wie oben:
Bearbeiten:
Es wird oft gesagt, dass die nächsten beiden Aussagen dasselbe tun:
Nun, nicht genau. Die erste ist ein Rohr aus
head
zubash
‚sread
builtin. Der Standard eines Prozesses ist der Standard eines anderen Prozesses.Die zweite Anweisung ist Umleitung und Prozessersetzung. Es wird von
bash
selbst gehandhabt . Es erstellt ein FIFO (Named Pipe<(...)
), mit demhead
die Ausgabe verbunden ist, und leitet es (<
) an denread
Prozess weiter.Bisher scheinen diese gleichwertig zu sein. Bei der Arbeit mit Variablen kann dies jedoch von Bedeutung sein. Im ersten Fall werden die Variablen nach der Ausführung nicht gesetzt. Im zweiten Fall sind sie in der aktuellen Umgebung verfügbar.
Jede Shell hat in dieser Situation ein anderes Verhalten. Siehe den Link, für den sie sind. In können
bash
Sie dieses Verhalten mit Befehlsgruppierung{}
, Prozessersetzung (< <()
) oder Here-Zeichenfolgen (<<<
) umgehen .quelle
{}
Ich habe es nur versucht()
. Warum startet das;
in Ihrem zweiten Beispiel nicht "einen neuen Befehlssatz"? Eigentlich verstehe ich das< <
Konstrukt überhaupt nicht.{}
wird der Rest der Zeile zu einem (Eins!) - Element der Pipeline, während innerhalb dieser Unterschale die Variablen verfügbar sind. Bei der Prozesssubstitution werden keine Pipes verwendet und daherbash
keine Subshells erzeugt.read
nur die erste Eingabezeile gelesen wird, ist nicht nur-n1
die redundante, sondern der gesamtehead
Befehl. Wenn Sie denhead
Befehl entfernen, können Sie auch die Pipeline entfernen. Dann wird esread A B C D E FOO < somefile
und es wird keine Unterschale, so dass die Variablen für den nächsten Befehl verfügbar bleiben.Um aus einem sehr nützlichen Artikel zu zitieren : wiki.bash-hackers.org :
Da die Antwort jetzt einige Male gegeben wurde, ist eine alternative Möglichkeit (unter Verwendung nicht eingebauter Befehle ...) folgende:
quelle
Wie Sie bemerkt haben, bestand das Problem darin, dass eine Pipe to
read
in einer Subshell ausgeführt wird.Eine Antwort ist, einen Heredoc zu verwenden :
Diese Methode ist insofern hilfreich, als sie sich in jeder POSIX-ähnlichen Shell genauso verhält.
quelle