Ich bin an bash
die integrierte read
Funktion von in while-Schleifen gewöhnt, zB:
echo "0 1
1 1
1 2
2 3" |\
while read A B; do
echo $A + $B | bc;
done
Ich habe an einem Projekt gearbeitet make
und es war sinnvoll, Dateien aufzuteilen und Zwischenergebnisse zu speichern. Infolgedessen zerlege ich häufig 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 das while
,
head -n1 somefile | read A B C D E FOO; [... use vars here ...]
Die gelesenen Variablen sind immer leer, wenn ich sie verwende. Mir ist dieses Verhalten von nie aufgefallen read
, da ich normalerweise while-Schleifen verwende, um viele ähnliche Zeilen zu verarbeiten. Wie kann ich bash
das read
integrierte von ohne while-Schleife verwenden? Oder gibt es eine andere (oder sogar bessere) Möglichkeit, eine einzelne Zeile in mehrere (!) Variablen einzulesen?
Abschluss
Die Antworten lehren uns, dass es sich um ein Problem des Geltungsbereichs handelt. Die Aussage
cmd0; cmd1; cmd2 | cmd3; cmd4
wird so interpretiert, dass die Befehle cmd0
, cmd1
, und cmd4
im selben Gültigkeitsbereich ausgeführt werden, während die Befehle cmd2
und cmd3
jeweils eine eigene Untershell und folglich unterschiedliche Gültigkeitsbereiche erhalten. Die ursprüngliche Shell ist die übergeordnete Shell beider Untershells.
Antwort1
Das liegt daran, dass der Teil, in dem Sie die Variablen verwenden, ein neuer Befehlssatz ist. Verwenden Sie stattdessen Folgendes:
head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }
{
Beachten Sie, dass in dieser Syntax nach dem ein Leerzeichen und ;
vor dem ein (Semikolon) stehen muss }
. -n1
Ist ebenfalls nicht erforderlich; read
liest nur die erste Zeile.
Zum besseren Verständnis kann Ihnen Folgendes helfen; es bewirkt dasselbe wie oben:
read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO
Bearbeiten:
Es wird oft gesagt, dass die nächsten beiden Anweisungen dasselbe bewirken:
head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)
Nun, nicht genau. Das erste ist eine Pipe von head
zu bash
's read
Builtin. Die Standardausgabe eines Prozesses zur Standardeingabe eines anderen Prozesses.
Die zweite Anweisung ist eine Umleitung und Prozessersetzung. Sie wird von bash
selbst gehandhabt. Sie erstellt eine FIFO (Named Pipe, <(...)
), head
an die die Ausgabe von angeschlossen ist, und leitet <
sie an den Prozess um ( ) read
.
Bisher scheinen diese gleichwertig zu sein. Aber beim Arbeiten mit Variablen kann es wichtig sein. Im ersten Fall werden die Variablen nach der Ausführung nicht gesetzt. Im zweiten sind sie in der aktuellen Umgebung verfügbar.
Jede Shell verhält sich in dieser Situation anders. Siehedieser Linkwofür sie sind. bash
Sie können dieses Verhalten mit Befehlsgruppierung {}
, Prozessersetzung ( < <()
) oder Here-Strings ( <<<
) umgehen.
Antwort2
Um aus einem sehr nützlichen Artikel zu zitierenwiki.bash-hackers.org:
Dies liegt daran, dass die Befehle der Pipe in Subshells ausgeführt werden, die die übergeordnete Shell nicht ändern können. Infolgedessen werden die Variablen der übergeordneten Shell nicht geändert (siehe Artikel:Bash und der Prozessbaum).
Da die Antwort nun schon einige Male gegeben wurde, besteht eine alternative Möglichkeit (unter Verwendung nicht integrierter Befehle ...) darin:
$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0
Antwort3
Wie Sie bemerkt haben, bestand das Problem darin, dass eine Pipe read
in einer Subshell ausgeführt wird.
Eine Möglichkeit besteht in der Verwendung eineshierdoc:
numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second
Der Vorteil dieser Methode besteht darin, dass sie sich in jeder POSIX-ähnlichen Shell gleich verhält.