Verwenden Sie das integrierte Lese-Feature von Bash ohne eine While-Schleife

Verwenden Sie das integrierte Lese-Feature von Bash ohne eine While-Schleife

Ich bin an bashdie integrierte readFunktion 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 makeund 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 bashdas readintegrierte 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 cmd4im selben Gültigkeitsbereich ausgeführt werden, während die Befehle cmd2und cmd3jeweils 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 }. -n1Ist ebenfalls nicht erforderlich; readliest 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 headzu bash's readBuiltin. Die Standardausgabe eines Prozesses zur Standardeingabe eines anderen Prozesses.

Die zweite Anweisung ist eine Umleitung und Prozessersetzung. Sie wird von bashselbst gehandhabt. Sie erstellt eine FIFO (Named Pipe, <(...)), headan 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. bashSie 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 readin 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.

verwandte Informationen