Frage Was ist der Unterschied zwischen "Umleitung" und "Rohr"?


Diese Frage klingt vielleicht ein bisschen dumm, aber ich kann den Unterschied zwischen Umleitung und Pfeifen wirklich nicht sehen.

Die Umleitung wird zum Umleiten des stdout / stdin / stderr, z. ls > log.txt.

Pipes werden verwendet, um die Ausgabe eines Befehls als Eingabe für einen anderen Befehl, z.B. ls | grep file.txt.

Aber warum gibt es zwei Operatoren für die gleiche Sache?

Warum schreibst du nicht einfach? ls > grep Um die Ausgabe zu übergeben, ist das nicht auch eine Art Umleitung? Was ich vermisse?


173
2017-08-07 13:22


Ursprung




Antworten:


Pipe wird verwendet, um die Ausgabe an eine andere zu übergeben Programm oder Dienstprogramm.

Redirect wird verwendet, um die Ausgabe an entweder a Datei oder Stream.

Beispiel: thing1 > thing2 vs thing1 | thing2

thing1 > thing2

  1. Ihre Shell wird das genannte Programm ausführen thing1
  2. Alles das thing1 Die Ausgaben werden in eine Datei namens thing2. (Hinweis - wenn thing2 existiert, wird überschrieben

Wenn Sie die Ausgabe aus dem Programm übergeben möchten thing1 zu einem Programm namens thing2könnten Sie Folgendes tun:

thing1 > temp_file && thing2 < temp_file

was würde

  1. laufe Programm namens thing1
  2. Speichern Sie die Ausgabe in einer Datei mit dem Namen temp_file
  3. laufe Programm namens thing2, so zu tun, als ob die Person an der Tastatur den Inhalt von temp_file als Eingabe.

Aber das ist klobig, also haben sie Pipes als eine einfachere Möglichkeit dafür gemacht. thing1 | thing2 macht das selbe wie thing1 > temp_file && thing2 < temp_file

BEARBEITEN, um weitere Details zur Frage im Kommentar bereitzustellen:

Ob > versucht wurde, sowohl "zum Programm" als auch "in Datei schreiben" zu sein, könnte es Probleme in beiden Richtungen verursachen.

Erstes Beispiel: Sie versuchen, in eine Datei zu schreiben. Es existiert bereits eine Datei mit dem Namen, den Sie überschreiben möchten. Die Datei ist jedoch ausführbar. Vermutlich würde es versuchen, diese Datei auszuführen und die Eingabe zu übergeben. Sie müssten etwas tun, wie die Ausgabe in einen neuen Dateinamen schreiben und dann die Datei umbenennen.

Zweites Beispiel: Wie Florian Diesch sagte, was ist, wenn es einen anderen Befehl im System mit dem gleichen Namen gibt (also im Ausführungspfad). Wenn Sie beabsichtigen, eine Datei mit diesem Namen in Ihrem aktuellen Ordner zu erstellen, würden Sie feststecken.

Drittens: Wenn Sie einen Befehl falsch eingeben, warnt er Sie nicht, dass der Befehl nicht existiert. Gerade jetzt, wenn du tippst ls | gerp log.txt es wird dir sagen bash: gerp: command not found. Ob > gemeint war beides, es würde einfach eine neue Datei für dich erstellen (dann warne davor, dass es nicht weiß, was damit zu tun ist) log.txt).


194
2017-08-07 13:30



Vielen Dank. Du erwähntest thing1 > temp_file && thing2 < temp_file um mit Rohren einfacher zu machen. Aber warum nicht wiederverwenden? > Betreiber, um dies zu tun, z.B. thing1 > thing2 für Befehle thing1 und thing2 ? Warum ein zusätzlicher Operator? | ? - John Threepwood
"Nehmen Sie die Ausgabe und schreiben Sie es in eine Datei" ist eine andere Aktion als "Nehmen Sie die Ausgabe und übergeben Sie es an ein anderes Programm". Ich werde mehr Gedanken in meine Antwort bearbeiten ... - David Oneill
@JohnThreepwood Sie haben unterschiedliche Bedeutungen. Was, wenn ich etwas in eine Datei mit dem Namen umleiten wollte? less, beispielsweise? thing | less und thing > less sind vollkommen verschieden, da sie verschiedene Dinge tun. Was Sie vorschlagen, würde eine Mehrdeutigkeit schaffen. - Darkhogg
Ist es richtig zu sagen, dass "thing1> temp_file" lediglich syntaktischer Zucker für "thing1 | tee temp_file" ist? Seit ich etwas über Tee herausgefunden habe, benutze ich fast nie Weiterleitungen. - Sridhar-Sarnobat
@ Sridhar-Sarnobat nein, die tee Befehl macht etwas anderes. tee schreibt die Ausgabe auf den Bildschirm (stdout) und die Datei. Umleitung tut es nur die Datei. - David Oneill


Wenn die Bedeutung von foo > bar würde davon abhängen, ob es einen Befehl namens gibt bar das würde die Umleitung viel schwieriger und fehleranfälliger machen: Jedes Mal, wenn ich in eine Datei umleiten möchte, musste ich zuerst prüfen, ob ein Befehl wie meine Zieldatei benannt ist.


19
2017-08-07 13:40



Dies wäre nur ein Problem, wenn Sie schreiben bar in einem Verzeichnis, das zu deinem gehört $PATH Umgebungsvariable Wenn Sie in etwas wie / bin sind, könnte ot ein Problem sein. Aber selbst dann, bar müsste eine ausführbare Berechtigung haben, so dass die Shell nicht nur nach einer ausführbaren Datei sucht bar aber kann es tatsächlich ausführen. Und wenn es um das Überschreiben vorhandener Dateien geht, noclober Die Shell-Option sollte das Überschreiben vorhandener Dateien in Umleitungen verhindern. - Sergiy Kolodyazhnyy


Es gibt einen entscheidenden Unterschied zwischen den beiden Betreibern:

  1. ls > log.txt -> Dieser Befehl sendet die Ausgabe an die Datei log.txt.

  2. ls | grep file.txt -> Dieser Befehl sendet die Ausgabe des ls an den Befehl grep über die Pipe (|), und der Befehl grep sucht in der Eingabe, die ihm vom vorherigen Befehl zur Verfügung gestellt wurde, nach file.txt.

Wenn Sie die gleiche Aufgabe mit dem ersten Szenario ausführen müssten, wäre es:

ls > log.txt; grep 'file.txt' log.txt

Also eine Pfeife (mit |) wird verwendet, um die Ausgabe an andere Befehle zu senden, während die Umleitung (mit >) wird verwendet, um die Ausgabe in eine Datei umzuleiten.


11
2017-08-07 13:32





Aus dem Unix- und Linux-Systemverwaltungshandbuch:

Umleitung

Die Shell interpretiert die Symbole <,> und >> als Anweisungen zum Umleiten von a Kommandos Eingabe oder Ausgabe zu oder von a Datei.

Rohre

Um den STDOUT von einem zu verbinden Befehl zur STDIN von Ein weiterer Verwende die | Symbol, allgemein bekannt als eine Pfeife.

Also meine Interpretation ist: Wenn es Befehl zu befehlen ist, benutze eine Pipe. Wenn Sie in oder aus einer Datei ausgeben, verwenden Sie die Weiterleitung.


9
2018-02-16 00:40





Es gibt einen großen syntaktischen Unterschied zwischen den beiden:

  1. Eine Umleitung ist ein Argument für ein Programm
  2. Eine Pipe trennt zwei Befehle

Sie können sich Umleitungen wie folgt vorstellen: cat [<infile] [>outfile]. Das bedeutet Ordnung ist nicht wichtig: cat <infile >outfile ist das gleiche wie cat >outfile <infile. Sie können Redirects sogar mit anderen Argumenten mischen: cat >outfile <infile -b und cat <infile -b >outfile sind beide völlig in Ordnung. Sie können auch mehr als einen Eingang oder Ausgang aneinanderreihen (Eingänge werden sequentiell gelesen und alle Ausgänge werden in jede Ausgangsdatei geschrieben): cat >outfile1 >outfile2 <infile1 <infile2. Das Ziel oder die Quelle einer Weiterleitung kann entweder ein Dateiname oder der Name eines Streams sein (wie & 1, zumindest in bash).

Aber Pipes trennen einen Befehl vollständig von einem anderen Befehl, Sie können sie nicht mit Argumenten mischen:

[command1] | [command2]

Die Pipe nimmt alles, was in die Standardausgabe von Befehl1 geschrieben wurde, und sendet es an die Standardeingabe von Befehl2.

Sie können auch Rohrleitungen und Umleitungen kombinieren. Beispielsweise:

cat <infile >outfile | cat <infile2 >outfile2

Der Erste cat liest Zeilen aus der Infile, schreibt dann gleichzeitig jede Zeile in die Ausgabedatei und sendet sie an die zweite cat.

In dieser Sekunde catDie Standardeingabe liest zuerst aus der Pipe (den Inhalt von infile), liest dann aus infile2 und schreibt jede Zeile in outfile2. Nach dem Ausführen wird outfile eine Kopie von infile sein, und outfile2 wird infile gefolgt von infile2 enthalten.

Schließlich, Sie tun tatsächlich etwas sehr ähnlich zu Ihrem Beispiel mit "Hier String" Umleitung (nur Bash-Familie) und Backticks:

grep blah <<<`ls`

wird das gleiche Ergebnis wie geben

ls | grep blah

Aber ich denke, die Umleitungsversion liest zuerst alle Ausgaben von ls in einen Puffer (im Speicher) und füttert dann diesen Puffer, um jeweils eine Zeile zu grepeln, wohingegen die piped Version jede Zeile von ls nimmt, wenn sie erscheint. und übergebe diese Zeile an Grep.


3
2017-08-23 22:24



Nitpick: Ordnung in Umleitung, wenn Sie ein fd zu einem anderen umleiten: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah Außerdem wird bei der Umleitung zu einer Datei nur die letzte Umleitung verwendet. echo yes >/tmp/blah >/tmp/blah2 werde nur schreiben /tmp/blah2. - muru
Redirect ist kein Argument für das Programm. Das Programm wird nicht wissen oder sich kümmern, wo seine Ausgabe geht (oder Eingang kommt von). Es ist nur eine Art zu erzählen, wie man Dinge arrangiert, bevor man das Programm startet. - Alois Mahdal


Um zu den anderen Antworten hinzuzufügen, gibt es auch subtile semantische Unterschiede - z. Pipes schließen schneller als Redirects:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

Im ersten Beispiel, wenn der erste Aufruf an head beendet, schließt es das Rohr und seq beendet, so dass für die Sekunde keine Eingabe verfügbar ist head.

Im zweiten Beispiel verbraucht head die erste Zeile, aber wenn sie geschlossen wird, ist es die eigene stdin  Rohr, die Datei bleibt für den nächsten Aufruf geöffnet.

Das dritte Beispiel zeigt, dass wenn wir verwenden read Um zu vermeiden, dass die Pipe geschlossen wird, ist sie immer noch im Subprozess verfügbar.

Also ist der "Stream" die Sache, durch die wir Daten umleiten (stdin etc), und ist in beiden Fällen gleich, aber die Pipe verbindet Streams von zwei Prozessen, bei denen eine Umleitung einen Stream zwischen einem Prozess und einer Datei verbindet kann die Quelle der Ähnlichkeiten und Unterschiede sehen.

P.S. Wenn Sie so neugierig und / oder überrascht von diesen Beispielen sind, wie ich war, können Sie sich weiter einarbeiten trap um zu sehen, wie sich die Prozesse auflösen, z.

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Manchmal schließt der erste Prozess vorher 1 wird gedruckt, manchmal später.

Ich fand es auch interessant zu verwenden exec <&- Schließen Sie den Stream aus der Umleitung, um das Verhalten der Pipe anzunähern (wenn auch mit einem Fehler):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

2
2018-06-05 00:54



"Wenn der erste Anruf zum Kopf beendet wird, schließt es die Pipe" Dies ist aus zwei Gründen tatsächlich ungenau. One, (head -n1; head -n1) ist eine Untershell mit zwei Befehlen, von denen jeder das Leseende der Pipe als Deskriptor 0 erbt, und somit hat subshell UND jeder Befehl diesen Dateideskriptor geöffnet. Zweiter Grund, du kannst das mit strace -f bash -c 'seq 5 | sehen (Kopf -n1; Kopf -n1). Der erste Kopf schließt also nur seine Kopie des Dateideskriptors - Sergiy Kolodyazhnyy
Das dritte Beispiel ist auch ungenau, weil read verbraucht nur die erste Zeile (das ist ein Byte für 1 und Zeilenumbruch). seq gesendet insgesamt 10 Bytes (5 Nummern und 5 Zeilenumbrüche). Es bleiben also 8 Bytes im Pipe Buffer, und das ist der zweite Grund head funktioniert - im Pipe Buffer sind noch Daten verfügbar. Übrigens, der Kopf wird nur verlassen, wenn 0 Bytes gelesen werden, so wie in head /dev/null - Sergiy Kolodyazhnyy
Danke für die Klarstellung. Verstehe ich das richtig seq 5 | (head -n1; head -n1)der erste Aufruf leert die Pipe, so dass sie immer noch in einem offenen Zustand existiert, aber keine Daten für den zweiten Aufruf enthält head? Der Unterschied im Verhalten zwischen der Pipe und der Umleitung liegt also darin, dass head alle Daten aus der Pipe zieht, aber nur die 2 Zeilen aus der Datei behandeln? - Julian de Bhal
Das ist richtig. Und es ist etwas, mit dem man sehen kann strace Befehl gab ich im ersten Kommentar. Bei der Umleitung befindet sich die tmp-Datei auf der Festplatte, wodurch sie gesucht werden kann (weil sie sie verwenden) lseek() syscall - Befehle können um die Datei vom ersten Byte bis zum letzten springen, wie sie wollen. Aber Rohre sind sequentiell und nicht suchbar. Die einzige Möglichkeit für den Kopf, seine Aufgabe zu erledigen, besteht darin, zuerst alles zu lesen oder, wenn die Datei groß ist, einen Teil davon über das RAM zu kartieren mmap() Anruf. Ich habe mal mein eigenes gemacht tail in Python und lief genau in dasselbe Problem. - Sergiy Kolodyazhnyy
Es ist auch wichtig daran zu denken, dass das Leseende der Pipe (Dateideskriptor) zuerst an die Subshell übergeben wird (...), und die Subshell erstellt eine Kopie ihrer eigenen stdin für jeden Befehl darin (...). Sie werden also technisch vom selben Objekt gelesen. Zuerst head  denkt, dass es von seiner eigenen stdin liest. Zweite head denkt, dass es eine eigene stdin hat. Aber in Wirklichkeit ist ihre fd # 1 (stdin) nur eine Kopie derselben fd, die das Ende der Pipe ist. Außerdem habe ich eine Antwort geschrieben, vielleicht hilft es also, die Dinge zu klären. - Sergiy Kolodyazhnyy


Anmerkung: Die Antwort reflektiert mein eigenes Verständnis dieser Mechanismen auf dem neuesten Stand, angesammelt über Forschung und Lesen der Antworten durch die Peers auf dieser Site und unix.stackexchange.comund wird im Laufe der Zeit aktualisiert werden. Zögern Sie nicht, Fragen zu stellen oder Verbesserungen in den Kommentaren vorzuschlagen. Ich schlage auch vor, Sie versuchen zu sehen, wie syscalls in der Shell arbeitet strace Befehl. Lassen Sie sich auch nicht von der Vorstellung von Internals oder Syscalls einschüchtern - Sie müssen nicht wissen oder in der Lage sein, sie zu benutzen, um zu verstehen, wie Shell Dinge macht, aber sie helfen definitiv beim Verständnis.

TL; DR

  • | Pipes sind keinem Eintrag auf der Festplatte zugeordnet, Deshalb habe ich keine Inode Anzahl der Disk - Dateisystem (aber inode haben in Pfeifen virtuelles Dateisystem in kernel-space), aber Umleitungen beinhalten oft Dateien, die Platteneinträge haben und daher entsprechende inode haben.
  • Rohre sind nicht lseek()'kann also Befehle können einige Daten nicht lesen und dann zurückspulen, aber wenn Sie mit umleiten > oder < Normalerweise ist es eine Datei, die ist lseek() fähiges Objekt, so können Befehle beliebig navigieren.
  • Weiterleitungen sind Manipulationen an Dateideskriptoren, die viele sein können; Pipes haben nur zwei Dateideskriptoren - einen für den linken Befehl und einen für den rechten Befehl
  • Umleitung auf Standard-Streams und Pipes sind beide gepuffert.
  • Rohre beinhalten fast immer Forking, Umleitungen - nicht immer
  • Pipes befassen sich immer mit Dateideskriptoren, Umleitungen - verwenden entweder tatsächliche Dateien mit dem Dateinamen auf der Festplatte oder Dateideskriptoren.
  • Pipes sind Inter-Process Communication-Methode, während Umleitungen nur Manipulationen an geöffneten Dateien oder dateiähnlichen Objekten sind
  • beide beschäftigen sich dup2() syscalls unter der Haube, um Kopien von Dateideskriptoren bereitzustellen, wo der tatsächliche Datenfluss stattfindet.
  • Umleitungen können "global" mit angewendet werden exec integrierter Befehl (siehe Dies und Dies ), wenn du es tust exec > output.txt jeder Befehl wird schreiben output.txt von da an. | Pipes werden nur für den aktuellen Befehl angewendet (was entweder einen einfachen Befehl oder eine Untershell-ähnliche Anweisung bedeutet) seq 5 | (head -n1; head -n2)oder zusammengesetzte Befehle.
  • Wenn die Umleitung auf Dateien vorgenommen wird, Dinge wie echo "TEST" > file und echo "TEST" >> file beide benutzen open() syscall für diese Datei (siehe auch) und einen Dateideskriptor von ihm erhalten, um ihn zu übergeben dup2(). Rohre | benutz nur pipe() und dup2() syscall.

Einführung

Um zu verstehen, wie sich diese beiden Mechanismen unterscheiden, ist es notwendig, ihre wesentlichen Eigenschaften, die Geschichte hinter den beiden und ihre Wurzeln in der Programmiersprache C zu verstehen. In der Tat wissen, was Dateideskriptoren sind und wie dup2() und pipe() Systemaufrufe Arbeit ist ebenso wichtig wie lseek(). Shell ist dazu gedacht, diese Mechanismen für den Benutzer abstrakt zu machen, aber tiefer zu graben als die Abstraktion hilft, die wahre Natur des Shell-Verhaltens zu verstehen.

Die Ursprünge von Umleitungen und Pfeifen

Nach Dennis Ritches Artikel Prophetische Petroglyphen, Rohre stammen von a 1964 internes Memo durch Malcolm Douglas McIlroy, zu der Zeit, als sie arbeiteten Multics Betriebssystem. Zitat:

Um meine stärksten Anliegen auf den Punkt zu bringen:

  1. Wir sollten einige Möglichkeiten haben, Programme wie Gartenschlauch zu verbinden - ein anderes Segment einzuschrauben, wenn es notwendig wird, Daten auf andere Weise zu massieren. Dies ist auch der Weg von IO.

Es ist offensichtlich, dass zu der Zeit, als Programme auf die Festplatte schreiben konnten, dies jedoch ineffizient war, wenn die Ausgabe groß war. Um Brian Kernighans Erklärung zu zitieren Unix-Pipeline Video :

Erstens müssen Sie nicht ein großes, umfangreiches Programm schreiben - Sie haben bereits kleinere Programme, die möglicherweise bereits Teile des Jobs erledigen ... Ein anderes ist, dass die Menge der Daten, die Sie verarbeiten, nicht passen würde Sie haben es in einer Datei gespeichert ... weil Sie sich erinnern, wir sind zurück in den Tagen, als Festplatten auf diesen Dingen, wenn Sie Glück hatten, ein Megabyte oder zwei Daten hatten ... Also musste die Pipeline niemals die gesamte Ausgabe instanziieren .

So ist der konzeptionelle Unterschied offensichtlich: Pipes sind ein Mechanismus, um Programme miteinander reden zu lassen. Redirections - sind eine Art zu schreiben in Datei auf der Basisebene. In beiden Fällen macht Shell diese beiden Dinge einfach, aber unter der Haube tut sich eine Menge.

Gehen Sie tiefer: syscalls und interne Arbeiten der Shell

Wir beginnen mit der Vorstellung von Dateideskriptor. Dateideskriptoren beschreiben im Grunde eine offene Datei (ob es sich um eine Datei auf der Festplatte oder im Speicher oder um eine anonyme Datei handelt), die durch eine Ganzzahl dargestellt wird. Die Zwei Standard-Datenströme  (stdin, stdout, stderr) sind die Dateideskriptoren 0,1 bzw. 2. Woher kommen sie ? Nun, in Shell-Befehlen werden die Dateideskriptoren von ihrer Eltern-Shell geerbt. Und es gilt im Allgemeinen für alle Prozesse - Kindprozess erbt die Dateideskriptoren der Eltern. Zum Dämonen Es ist üblich, alle geerbten Dateideskriptoren zu schließen und / oder an andere Stellen weiterzuleiten.

Zurück zur Weiterleitung. Was ist das wirklich? Es ist ein Mechanismus, der der Shell vorschreibt, Dateideskriptoren für den Befehl vorzubereiten (da Umleitungen von der Shell ausgeführt werden, bevor der Befehl ausgeführt wird) und sie dort zu zeigen, wo der Benutzer sie vorgeschlagen hat. Das Standarddefinition der Ausgabeumleitung ist

[n]>word

Das [n] Es gibt die Datei-Deskriptor-Nummer. Wenn Sie das tun echo "Something" > /dev/null die Nummer 1 ist dort impliziert, und echo 2> /dev/null.

Unter der Haube geschieht dies durch Duplizierung des Dateideskriptors via dup2() Systemaufruf. Lass uns nehmen df > /dev/null. Die Shell erstellt einen untergeordneten Prozess, in dem df läuft, aber vorher wird es geöffnet /dev/null als Dateibeschreibung # 3, und dup2(3,1) wird ausgegeben, was eine Kopie des Datei-Deskriptors 3 und der Kopie wird 1. Sie wissen, wie Sie zwei Dateien haben file1.txt und file2.txtund wenn du es tust cp file1.txt file2.txt Sie haben zwei gleiche Dateien, aber Sie können sie unabhängig voneinander manipulieren? Das ist irgendwie das gleiche, was hier passiert. Oft kann man das vor dem Laufen sehen bash Wird besorgt dup(1,10) um einen Kopiendateideskriptor # 1 zu erstellen, der ist stdout (und diese Kopie wird fd # 10 sein), um es später wiederherzustellen. Wichtig ist dies zu beachten integrierte Befehle (die Teil der Shell selbst sind und keine Datei haben /bin oder anderswo) oder einfache Befehle in nicht-interaktiven Shell, die Shell erstellt keinen untergeordneten Prozess.

Und dann haben wir Dinge wie [n]>&[m] und [n]&<[m]. Dies ist die Duplizierung von Dateideskriptoren, die denselben Mechanismus wie dup2() nur jetzt ist es in der Shell-Syntax, bequem für den Benutzer verfügbar.

Eines der wichtigen Dinge, die bei der Umleitung zu beachten sind, ist, dass ihre Reihenfolge nicht festgelegt ist, sondern wichtig ist, wie Shell den gewünschten Benutzer interpretiert. Vergleichen Sie Folgendes:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

Die praktische Verwendung dieser in Shell-Skripten kann vielseitig sein:

und viele weitere.

Klempner mit pipe() und dup2()

Wie werden Pipes erstellt? Über pipe() syscall, die als Eingabe ein Array (aka Liste) genannt wird pipefd von zwei Arten von Typ int (ganze Zahl). Diese zwei Ganzzahlen sind Dateideskriptoren. Das pipefd[0] wird das Leseende der Pipe und sein pipefd[1] wird das Schreibende sein. Also rein df | grep 'foo', grep wird eine Kopie von bekommen pipefd[0] und df wird eine Kopie von bekommen pipefd[1]. Aber wie ? Natürlich mit der Magie von dup2() syscall. Zum df in unserem Beispiel sagen wir mal pipefd[1] hat # 4, so wird die Shell ein Kind machen, tun dup2(4,1) (Erinnere dich an meine cp Beispiel?), und dann tun execve() tatsächlich laufen df. Natürlich, df Erbt den Dateideskriptor # 1, wird aber nicht bemerken, dass er nicht mehr auf Terminal zeigt, sondern tatsächlich auf fd # 4, was eigentlich das Schreibende der Pipe ist. Natürlich wird dasselbe mit auftreten grep 'foo' außer mit unterschiedlichen Anzahlen von Dateideskriptoren.

Nun, interessante Frage: könnten wir Rohre machen, die auch fd # 2 umleiten, nicht nur fd # 1? Ja, genau das ist es |&tut in bash. Der POSIX-Standard erfordert Shell-Befehlssprache zur Unterstützung df 2>&1 | grep 'foo' Syntax für diesen Zweck, aber bash tut |& auch.

Es ist wichtig zu beachten, dass Pipes immer mit Dateideskriptoren umgehen. Es existiert FIFO oder benanntes Rohr, die einen Dateinamen auf der Festplatte hat und Sie verwenden es als Datei, aber verhält sich wie eine Pipe. Aber die | Arten von Pipes sind so genannte anonyme Pipe - sie haben keinen Dateinamen, weil sie wirklich nur zwei Objekte miteinander verbunden sind. Die Tatsache, dass wir nicht mit Dateien zu tun haben, macht auch eine wichtige Implikation: Rohre sind nicht lseek()'fähig. Dateien im Speicher oder auf der Festplatte sind statisch - Programme können sie verwenden lseek() syscall, um zu Byte 120 zu springen, dann zurück zu Byte 10, dann weiter den ganzen Weg bis zum Ende. Pipes sind nicht statisch - sie sind sequenziell und daher können Sie Daten, die Sie von ihnen erhalten, nicht zurückspulen lseek(). Dies macht einige Programme darauf aufmerksam, ob sie aus einer Datei oder aus einer Pipe lesen und somit die notwendigen Anpassungen für eine effiziente Leistung vornehmen können. mit anderen Worten, a prog kann erkennen, ob ich das tue cat file.txt | prog oder prog < input.txt. Ein echtes Arbeitsbeispiel dafür ist Schwanz.

Die anderen zwei sehr interessante Eigenschaft von Rohren ist, dass sie einen Puffer haben, der Unter Linux sind 4096 Bytesund sie haben tatsächlich eine Dateisystem wie im Linux-Quellcode definiert ! Sie sind nicht einfach ein Objekt für die Weitergabe von Daten, sie sind selbst eine Datenstruktur! Da es Pipefs-Dateisystem gibt, das sowohl Pipes als auch FIFOs verwaltet, Rohre haben einen Inode Nummer auf ihrem jeweiligen Dateisystem:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Unter Linux sind Pipes unidirektional, genau wie Umleitungen. Bei einigen Unix-ähnlichen Implementierungen gibt es bidirektionale Pipes. Obwohl mit Magie von Shell-Skripten können Sie machen bidirektionale Pipes unter Linux auch.

Siehe auch:


2
2017-09-12 09:26





Ich habe heute in C ein Problem damit. Im Wesentlichen haben Pipes eine andere Semantik als Redirects, selbst wenn sie gesendet werden stdin. Ich denke, angesichts der Unterschiede sollten die Pfeifen irgendwo anders hingehen stdin, damit stdin und lasst es uns anrufen stdpipe (um ein beliebiges Differential zu erzeugen) kann auf verschiedene Arten gehandhabt werden.

Bedenken Sie. Wenn ein Programm an ein anderes Programm ausgegeben wird fstat scheint Null zurück zu geben st_size Trotz ls -lha /proc/{PID}/fd zeigt, dass es eine Datei gibt. Beim Umleiten einer Datei ist dies nicht der Fall (zumindest bei debian wheezy, stretch und jessie Vanille und Ubuntu 14.04, 16.04Vanille.

Wenn du cat /proc/{PID}/fd/0 Mit einer Umleitung können Sie so oft wiederholen wie Sie möchten. Wenn Sie dies mit einer Pipe tun, werden Sie feststellen, dass Sie beim zweiten Ausführen der Aufgabe nacheinander die gleiche Ausgabe erhalten.


1
2017-10-26 16:17