Ausgabereihenfolge mit Prozessersetzung

11

Dies ist, was ich normalerweise mache, um eine Datei auszuführen grepund wczu bearbeiten, ohne sie zweimal scannen zu müssen

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Dies ergibt jedoch

EXEC LITERAL
32

manchmal und

32
EXEC LITERAL

zu anderen Zeiten. (Die Ausgabe von grepgeht der Ausgabe von wcin der ersten Instanz voraus und umgekehrt in der zweiten.)

Auf der anderen Seite mit Umleitungen und Dateideskriptoren

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1 

Ich scheine immer zu bekommen

EXEC LITERAL
32

Ich bevorzuge, dass die Ausgabereihenfolge vorhersehbar ist, aber ist sie mit dem zweiten Ansatz garantiert?

iruvar
quelle

Antworten:

4

Sowohl

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Und:

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1

Alle tee, grepund wcwerden gleichzeitig gestartet. Was dann zählt, ist was am Ende passiert.

wcdruckt das Ergebnis nur, wenn das Dateiende auf seiner Standardeingabe angezeigt wird. Im ersten Fall, das ist , wenn teeAusfahrten, weil dann teeseine schließen wird fdam anderen Ende des Rohres , das wcaus (Gestartet von Prozess - Substitution) liest. Dafür gibt es keine Garantiegrep bis zu diesem Zeitpunkt alle Eingaben gelesen wurden, geschweige denn die Ausgaben geschrieben wurden (da Pipes eine ziemlich große Datenmenge aufnehmen können und dies wcwahrscheinlich schneller sein wird als grep).

Im zweiten Fall wcwird das Dateiende angezeigt, wenn alle Schreiber der Pipe, von der gelesen wird, ihr Pipe-Ende geschlossen haben. In diesem Fall gibt es jedoch mehrere Autoren. tee(über sein fd open on /dev/fd/3und über sein fd 3) und grepdas auch seine fd3 offen für das Rohr hat wc(obwohl es es nicht benutzt, geschweige denn darauf schreibt). Das Innere {wird wahrscheinlich einen zusätzlichen Subshell-Prozess verursachen, bei dem auch eine fd3 geöffnet ist und auf beide teeund gewartet wird grep.

Das bedeutet, dass wcdie Zeilennummer erst nach dem grepBeenden geschrieben wird.

Hatten Sie es richtig geschrieben, indem Sie die FDS geschlossen haben, die nicht geöffnet werden mussten:

{ { <file.txt tee /dev/fd/3 4>&- | 
   grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1

Dann wäre die Reihenfolge in Shells, die den Subshell-Prozess optimieren, nicht garantiert worden. Die einzige Shell, die ich kenne, ist ksh93aberksh93 Verwendungen Buchsenpaare für Rohre, so /dev/fd/3wird nicht da zumindest unter Linux arbeiten.

Um zu sehen , welche Prozesse ausgeführt werden , können Sie ersetzen grepmit ps:

$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
  PID TTY          TIME CMD
 8727 pts/5    00:00:00 bash
 8815 pts/5    00:00:00   bash
 8817 pts/5    00:00:00     tee
 8818 pts/5    00:00:00     ps
 8816 pts/5    00:00:00   wc

Mit bashkönnen Sie diesen zusätzlichen Shell-Prozess sehen, und Sie können sehen, dass auch die Pipe auf fd 3 geöffnet ist mit:

$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND  PID PGID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash    9843 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
tee     9845 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
lsof    9846 9842 chazelas    3r   DIR    0,3        0      1 /proc
Stéphane Chazelas
quelle
Vielen Dank. Was bedeutet in Ihrem "richtigen Beispiel", grep LITERAL >&4 3>&- 4>&-dass der fd 4 sowohl verwendet als auch geschlossen zu sein scheint?
iruvar
@ 1_CR, nach >&4, kurz für 1>&4, grepfd 1 und 4 zeigen auf dieselbe Ressource (die anfängliche Standardausgabe der Shell). grepmuss seine fd 4 für nichts offen haben. Es macht nichts damit, also schließen wir es mit4>&-
Stéphane Chazelas
Diese letzte Befehlszeile ist kryptische Magie.
-1

Um eine vorhersehbare Bestellung zu erhalten, verwenden Sie

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
Thorsten Staerk
quelle
Vielleicht war ich nicht klar genug. Ich meinte vorhersehbare Reihenfolge in Bezug auf die Reihenfolge der Befehlsausgaben (dh Ausgabe von grep vor Ausgabe von wc). Ich brauche die kombinierte Ausgabe nicht sortiert
iruvar
Ich habe gerade gnu.org/software/bash/manual/bashref.html#Command-Grouping gefunden und erfahre , dass Sie mit den Operatoren {} (in diesem Fall) sicherstellen, dass Sie zuerst <file.txt tee / dev / fd ausführen / 3 | grep LITERAL> & 4; und wenn dies erledigt ist, rufst du wc an, um deine ursprüngliche Frage zu beantworten, ja, es ist nach meinem Verständnis garantiert
Thorsten Staerk
1
@ThorstenStaerk Könnten Sie die zusätzlichen Informationen, die Sie gefunden haben, zu Ihrer Antwort hinzufügen?
Terdon