In welcher Reihenfolge werden Pipe-Befehle ausgeführt?

89

Ich habe nie wirklich darüber nachgedacht, wie die Shell tatsächlich Pipe-Befehle ausführt. Ich habe immer gesagt, dass die „stdout eines Programms wird verrohrt in den stdin eines anderen,“ als eine Möglichkeit , über Rohre zu denken. Also dachte ich natürlich, dass im Falle von sagen wir, A | B, A würde zuerst ausgeführt, dann erhält B die Standardausgabe von A und verwendet die Standardausgabe von A als Eingabe.

Ich habe jedoch festgestellt, dass bei der Suche nach einem bestimmten Prozess in ps grep -v "grep" am Ende des Befehls eingefügt wird, um sicherzustellen, dass grep nicht in der endgültigen Ausgabe angezeigt wird. Dies bedeutet, dass im Befehl ps aux | grep "bash" | grep -v "grep", was bedeutet, dass ps wusste, dass grep ausgeführt wird und daher in der Ausgabe von ps enthalten ist. Aber wenn ps die Ausführung beendet, bevor die Ausgabe an grep weitergeleitet wird, woher wusste es dann, dass grep ausgeführt wird?

flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*"
PID TTY          TIME CMD
3773 pts/0    00:00:00 bash
3784 pts/0    00:00:00 ps
3785 pts/0    00:00:00 grep
action_potato
quelle
Warum nicht eine Antwort annehmen ?
Törzsmókus

Antworten:

64

Weitergeleitete Befehle werden gleichzeitig ausgeführt. Wenn Sie rennen ps | grep …, ist es das Glück der Auslosung (oder eine Frage der Funktionsweise der Shell in Kombination mit der Feinabstimmung des Schedulers tief im Inneren des Kernels), ob Sie zuerst beginnen psoder grepbeginnen, und auf jeden Fall fahren Sie fort gleichzeitig ausführen.

Dies wird sehr häufig verwendet, um dem zweiten Programm die Verarbeitung von Daten zu ermöglichen, die aus dem ersten Programm stammen, bevor das erste Programm seine Operation abgeschlossen hat. Zum Beispiel

grep pattern very-large-file | tr a-z A-Z

Beginnt, die übereinstimmenden Zeilen in Großbuchstaben anzuzeigen, noch bevor grepdas Durchlaufen der großen Datei abgeschlossen ist.

grep pattern very-large-file | head -n 1

zeigt die erste übereinstimmende Zeile an und beendet möglicherweise die Verarbeitung, bevor grepdas Lesen der Eingabedatei abgeschlossen ist.

Wenn Sie irgendwo gelesen haben, dass Pipe-Programme nacheinander ausgeführt werden, fliehen Sie aus diesem Dokument. Weitergeleitete Programme werden gleichzeitig ausgeführt und haben dies immer getan.

Gilles
quelle
7
Und das Coole an diesem Beispiel ist, dass wenn head die eine Zeile erhält, die es benötigt, es beendet wird und wenn grep dies bemerkt, es auch endet, ohne eine Menge weiterer Arbeit für nichts zu tun.
Joe
Ich vermute, es gibt eine Art IO-Puffer in Bezug auf die Pipe ... Woher weiß ich, dass die Größe in Bytes ist? Was möchte ich lesen, um mehr darüber zu erfahren? :)
n611x007
3
@naxa Eigentlich gibt es zwei Puffer. Es gibt den stdio- Puffer im grepProgramm und einen Puffer, der vom Kernel in der Pipe selbst verwaltet wird. Letzteres finden Sie unter Wie groß ist der Pipe-Puffer?
Gilles
49

Die Reihenfolge, in der die Befehle ausgeführt werden, spielt eigentlich keine Rolle und kann nicht garantiert werden. Abgesehen von den arcane Einzelheiten pipe(), fork(), dup()und execve()schafft die Schale zuerst das Rohr, die Leitung für die Daten , die zwischen den Prozessen fließen wird, und erzeugt dann die Prozesse mit den Enden des Rohres mit ihnen verbunden sind . Der erste Prozess, der ausgeführt wird, blockiert möglicherweise das Warten auf Eingaben vom zweiten Prozess oder das Warten auf den zweiten Prozess, um das Lesen von Daten von der Pipe zu starten. Diese Wartezeiten können beliebig lang sein und spielen keine Rolle. Unabhängig von der Reihenfolge, in der die Prozesse ausgeführt werden, werden die Daten schließlich übertragen und alles funktioniert.

Kyle Jones
quelle
5
Gute Antwort, aber das OP scheint zu glauben, dass die Prozesse nacheinander ablaufen. Hier können Sie klarstellen, dass die Prozesse gleichzeitig ablaufen und die Leitung wie eine Leitung zwischen Eimern ist, durch die (ungefähr) alle gleichzeitig Wasser fließt.
Keith
Danke für die Abklärung. Die Quellen, die ich gelesen habe, haben den Anschein, als würden Pipe-Programme nacheinander und nicht gleichzeitig ausgeführt.
action_potato
Um zu sehen, wie die Prozesse auf unbestimmte Weise beginnen, versuchen Sie, dies 1000 Mal auszuführen: echo -na> & 2 | echo b> & 2
Ole Tange
28

Das Risiko, ein totes Pferd zu schlagen, scheint das Missverständnis zu sein

    A | B

ist äquivalent zu

    A > temporäre_Datei 
    B < temporäre_Datei 
    rm temporäre_Datei

Aber als Unix erstellt wurde und Kinder mit Dinosauriern zur Schule fuhren, waren die Festplatten sehr klein, und es war üblich, dass ein ziemlich harmloser Befehl den gesamten freien Speicherplatz in einem Dateisystem verbrauchte. Wenn Bso etwas wäre , könnte die endgültige Ausgabe der Pipeline viel kleiner sein als diese Zwischendatei. Aus diesem Grund wurde die Pipe nicht als Abkürzung für das Modell "Führen Sie zuerst A und dann B mit Eingaben aus dem Ausgabemodell von A aus " entwickelt, sondern als Möglichkeit , sie gleichzeitig mit der Zwischendatei auszuführen und das Speichern der Zwischendatei zu vermeiden auf der Festplatte.grep some_very_obscure_stringBA

Scott
quelle
2
Dieser antwortet warum und bekommt daher meine Stimme.
Kleiner Urwald Kami
1

Normalerweise führen Sie dies unter Bash aus. Der Prozess läuft und startet gleichzeitig, wird aber von der Shell parallel ausgeführt. Wie ist es möglich?

  1. Wenn es nicht der letzte Befehl in der Pipe ist, erstellen Sie eine unbenannte Pipe mit zwei Sockets
  2. Gabel
  3. in child ordne stdin / stdout den Sockets zu, wenn es benötigt wird (für den ersten Prozess in pipe wird stdin nicht neu zugewiesen, das gleiche gilt für den letzten Prozess und sein stdout)
  4. in child EXEC angegebener Befehl mit Argumenten, die den ursprünglichen Shell-Code auslesen, aber alle von ihnen geöffneten Sockets belassen. Die ID des untergeordneten Prozesses wird nicht geändert, da dies derselbe untergeordnete Prozess ist
  5. Gleichzeitig mit dem Kind, aber parallel unter der Hauptschale, fahren Sie mit Schritt 1 fort.

Das System garantiert nicht, wie schnell Exec ausgeführt wird und der angegebene Befehl startet. Es ist unabhängig von der Shell, aber dem System. Das ist weil:

ps auxww| grep ps | cat

einmal zeigen grepund / oder psbefehl, und als nächstes jetzt. Es hängt davon ab, wie schnell der Kernel Prozesse mithilfe der System-Exec-Funktion startet.

Znik
quelle
1
Gleichzeitige Ausführung bedeutet, dass zwei oder mehr Prozesse innerhalb desselben Zeitrahmens ausgeführt werden, in der Regel mit einer gewissen Abhängigkeit zwischen ihnen. Parallele Ausführung bedeutet, dass zwei oder mehr Prozesse gleichzeitig ausgeführt werden (z. B. auf verschiedenen CPU-Kernen gleichzeitig). Parallelität ist nicht relevant für die Frage, noch ist "wie schnell" exec()ausgeführt, sondern wie die exec()Aufrufe und die Ausführung der Programme in einer Pipe verschachtelt sind .
Thomas Nyman