Gibt es eine Möglichkeit, Zeilen aus der Befehlsausgabe zu lesen?

8

Ich habe einen Vorverarbeitungsbefehl zum Ausgeben einer Datei

./preprocess.sh > preprocessed_file 

und das preprocessed_filewird so verwendet

while read line
do

    ./research.sh $line &

done < preprocessed_file 

rm -f preprocessed_file

Gibt es eine Möglichkeit, die Ausgabe an das while read lineTeil zu leiten, anstatt sie an die vorverarbeitete Datei auszugeben ? Ich denke, es sollte einen besseren Weg geben, als diese Temperatur zu verwenden preprocessed_file.

Marcus Thornton
quelle

Antworten:

8

Sie können die Bash- Prozess-Substitution verwenden :

while IFS= read -r line; do
  ./research.sh "$line" &
done < <(./preprocess.sh)

Einige Vorteile der Prozesssubstitution:

  • Temporäre Dateien müssen nicht gespeichert werden.
  • Bessere Leistung. Lesen von einem anderen Prozess oft schneller als Schreiben auf die Festplatte, dann wieder einlesen.
  • Sparen Sie Zeit bei der Berechnung, da diese gleichzeitig mit der Parameter- und Variablenerweiterung, der Befehlssubstitution und der arithmetischen Erweiterung ausgeführt wird
cuonglm
quelle
Was bedeuten die doppelten linken Pfeile (<<)?
Marcus Thornton
@MarcusThornton: <ist eine Umleitung, während <(...)die Prozesssubstitutionssyntax ist. Weitere Informationen finden Sie unter: gnu.org/software/bash/manual/html_node/… .
Cuonglm
Ich habs. <(...)ist ein Teil der Syntax.
Marcus Thornton
2
Es ist nicht unbedingt schneller. Denn beim Lesen aus einer Pipe readmuss jeweils ein Byte gelesen werden, während es beim Lesen größerer Blöcke die Dinge optimieren und beim Lesen aus einer regulären Datei rückwärts suchen kann. Am besten vermeiden Sie while readSchleifen, wenn möglich. Beachten Sie auch, dass Sie IFS= read -r linedie Zeile einlesen müssen $line. Und hier $linenicht in Anführungszeichen zu setzen (Aufruf des Operators split + glob) macht wahrscheinlich keinen Sinn.
Stéphane Chazelas
1
@mikeserv, Befehle puffern häufig ihre Ausgabe (im Gegensatz zu Vollpuffer), wenn sie an ein Terminal gesendet werden . Hier sage ich, dass die readeingebaute Shell beim Lesen aus einer Pipe jeweils ein Zeichen liest (unabhängig davon, was sich am anderen Ende der Pipe befindet und was readnicht bekannt ist). Dies ist einer der Gründe, warum while readSchleifen enorm langsam sind.
Stéphane Chazelas
15

Ja! Sie können eine Prozessleitung verwenden |.

./preprocess.sh |
    while IFS= read -r line
    do
        ./research.sh "$line" &
    done

Eine Prozessleitung übergibt die Standardausgabe ( stdout) eines Prozesses an die Standardeingabe ( stdin) des nächsten.

Sie können optional ein Zeilenumbruchzeichen nach a einfügen |und den Befehl auf die nächste Zeile erweitern.

Hinweis: a|bEntspricht b < <(a), jedoch ohne die magischen Dateien, und in einer besser lesbaren Reihenfolge, insbesondere wenn die Pipeline länger wird.

a|b|c ist äquivalent zu c < <(b < <(a))

und

a|b|c|d|e ist e < < (d < <(c < <(b < <(a))))

Strg-Alt-Delor
quelle
3
Hinweis: Diese Lösung mit der Pipe hat den Vorteil, dass sie portabler ist als die Prozessersetzung (wird von einigen POSIX-Shells wie dash nicht unterstützt). Noch Portabilität über die rechte Seite eines Rohres kann in einer Subshell ausgeführt werden (dies auf der Schale abhängt), so dass jede Nebenwirkung (wie zB Stellgrößen) nicht die Umgebung des Shell - Skript beeinflussen kann.
vinc17
Im Allgemeinen ist es sicherer, Variablenreferenzen wie $linein doppelte Anführungszeichen zu setzen (z. B. in Ihrem Skript ./research.sh "$line" &).
G-Man sagt "Reinstate Monica"
1
@ G-Man Möglicherweise nicht in diesem Zusammenhang. Wenn research.shmit dem Befehlszeilenargumentarray gearbeitet $linewird und z. B. "eins zwei" ist, mit der Absicht, dass das erste Argument "eins" und das zweite Argument "zwei" ist, $linewird das Zitieren dies unmöglich machen - stattdessen wird das erste Argument sein "eins zwei" und es wird keinen zweiten geben ...
Goldlöckchen
2
" a|bist gleichbedeutend mitb < <(a) " - nah, aber nicht ganz. In der Pipe-Version werden beide Seiten der Pipe in Subshells ausgeführt, während in der Prozessersetzungsversion nur der ersetzte Prozess in einer Subshell ausgeführt wird, jedoch aim Bereich der aktuell ausgeführten Shell-Ebene. Dies hat wichtige Auswirkungen auf den Umfang der Variablen, die ina
Digital Trauma