2015-12-17 6 views
12

Mit Bash-Prozess-Substitution möchte ich zwei verschiedene Befehle gleichzeitig in einer Datei ausführen. In diesem Beispiel ist es nicht notwendig, aber stellen Sie sich vor, dass "cat/usr/share/dict/words" eine sehr teure Operation ist, wie beispielsweise das Entpacken einer 50-GB-Datei.Falsche Ergebnisse mit Bash-Prozess-Substitution und Tail?

cat /usr/share/dict/words | tee >(head -1 > h.txt) >(tail -1 > t.txt) > /dev/null 

Nachdem dieser Befehl ich h.txt erwarten würde die erste Zeile der Wörter enthalten „A“ Datei, und t.txt die letzte Zeile der Datei „Zyzzogeton“ enthalten.

Was jedoch tatsächlich passiert, ist, dass h.txt "A" enthält, aber t.txt enthält "argillaceo", was etwa 5% in der Datei ist.

Warum geschieht das? Es scheint so, als ob entweder der "Tail" -Prozess vorzeitig beendet wird oder die Ströme durcheinander geraten.

einen weiteren ähnlichen Befehl wie das Lauf verhält sich wie erwartet:

cat /usr/share/dict/words | tee >(grep ^a > a.txt) >(grep ^z > z.txt) > /dev/null 

Nach diesem Befehl würde ich erwarten a.txt alle Wörter enthalten, die mit „a“ beginnen, während z.txt enthält alle von die Wörter, die mit "z" beginnen, was genau passiert ist.

Warum funktioniert das nicht mit "Tail", und mit welchen anderen Befehlen wird das nicht funktionieren?

+1

Ich denke, dies zu http://stackoverflow.com zusammenhängt/questions/4489139/bash-process-substitution-and-syncing, was darauf hindeutet, dass die Prozesse mit der Substitution beendet werden, sobald der äußere Befehl beendet wird, aber ehrlich gesagt kann ich nicht zeigen, dass dies das aktuelle Problem mit Befehlen I ist habe bisher probiert –

Antwort

10

Ok, was zu geschehen scheint, ist, dass, sobald der head -1 Befehl beendet es austritt und das verursacht tee eine SIGPIPE, um es an die Named Pipe zu schreiben versucht, dass der Prozess Substitution Einrichtung, die auch einen EPIPE und nach man 2 write erzeugt wird Generiere SIGPIPE im Schreibprozess, was dazu führt, dass tee beendet wird und das tail -1 zum sofortigen Beenden zwingt, und das cat auf der linken Seite erhält ebenfalls einen SIGPIPE.

Wir können diese ein wenig besser sehen, wenn wir ein bisschen mehr auf den Prozess mit head und machen die Ausgabe sowohl berechenbarer und auch geschrieben stderr, ohne sich auf die tee hinzufügen:

for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null 

die, wenn ich führen sie es mir den Ausgang gab:

1 
Head done 
2 

so hat es nur 1 mehr Iteration der Schleife vor allem (obwohl verlassen t.txt nur noch 1 hat drin). Wenn wir dann

tat
echo "${PIPESTATUS[@]}" 

wir sehen

141 141 

die this question Verbindungen zu SIGPIPE in sehr ähnlicher Weise zu dem, was wir hier sehen.

Die Coreutils-Betreuer haben dies als Beispiel für ihre tee "gotchas" für zukünftige Nachwelt hinzugefügt.

Für eine Diskussion mit den Devs darüber, wie diese passt in POSIX-Konformität können Sie die (geschlossene notabug) Bericht bei http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22195

sehen Wenn Sie 8 Zugriff auf GNU-Version haben.24 haben sie einige Optionen hinzugefügt (nicht in POSIX), die helfen können wie -p oder --output-error=warn. Ohne, dass Sie ein bisschen ein Risiko eingehen kann, aber die gewünschte Funktionalität in der Frage durch Abfangen und ignoriert SIGPIPE erhalten:

trap '' PIPE 
for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null 
trap - PIPE 

wird sowohl die erwarteten Ergebnisse in haben h.txt und t.txt, aber wenn etwas anderes passiert, dass Fahndungs ​​SIGPIPE Um richtig behandelt zu werden, hättest du bei diesem Ansatz kein Glück.

Eine andere Möglichkeit wäre Hacky Option t.txt auf Null, bevor die head Prozessliste Finish dann nicht zulassen, beginnen, bis sie nicht Null Länge:

> t.txt; for i in {1..10}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done"; while [ ! -s t.txt ]; do sleep 1; done) >(tail -1 > t.txt; date) >/dev/null 
+1

POSIX-spezifiziertes Verhalten für 'tee' ist, dass es weiter funktioniert, auch wenn einer seiner Leser austritt - wenn du also etwas Gegenteiliges siehst, ist das eigentlich ein Bug. –

+0

"Wenn ein Schreibvorgang in einen erfolgreich geöffneten Dateioperanden fehlschlägt, werden Schreibvorgänge in andere erfolgreich geöffnete Dateioperanden fortgesetzt und die Standardausgabe wird fortgesetzt, der Beendigungsstatus muss jedoch ungleich Null sein. Andernfalls gelten die in den Standardeinstellungen für die Dienstprogrammbeschreibung angegebenen Standardaktionen." - http://pubs.opengroup.org/onlinepubs/9699919799/utilities/tee.html –

+0

@CharlesDuffy gut die oben genannten Ergebnisse sind für eine ältere Version ich denke, 8.5, kann ich es erneut versuchen. Außerdem habe ich nicht tief genug geforscht, um zu wissen, ob sich die Prozesssubstitution als Closed File Handle darstellt oder ob SIGPIPE tatsächlich ausgelöst wird, wenn dieser Prozess endet. Ich hätte noch mehr zu tun, bevor ich einen Fehlerbericht übergebe: –

Verwandte Themen