Was ist der Unterschied zwischen "Redirection" und "Pipe"?

204

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

Die Umleitung wird verwendet, um stdout / stdin / stderr umzuleiten, z ls > log.txt.

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

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

Warum nicht einfach schreiben ls > grep, um die Ausgabe weiterzuleiten, ist das nicht auch eine Art Umleitung? Was mir fehlt

John Threepwood
quelle

Antworten:

223

Pipe wird verwendet, um die Ausgabe an ein anderes Programm oder Dienstprogramm weiterzuleiten .

Die Umleitung wird verwendet, um die Ausgabe an eine Datei oder einen Stream zu übergeben .

Beispiel: thing1 > thing2vsthing1 | thing2

thing1 > thing2

  1. Ihre Shell wird das genannte Programm ausführen thing1
  2. Alles, was thing1ausgegeben wird, wird in einer Datei namens platziert thing2. (Hinweis - falls thing2vorhanden, wird es überschrieben)

Wenn Sie die Ausgabe von program thing1an ein Programm übergeben möchten, das aufgerufen wird thing2, können Sie Folgendes tun:

thing1 > temp_file && thing2 < temp_file

was würde

  1. Führen Sie das genannte Programm aus thing1
  2. Speichern Sie die Ausgabe in einer Datei mit dem Namen temp_file
  3. Führen Sie das Programm mit dem Namen aus thing2und tun Sie so, als ob die Person an der Tastatur den Inhalt temp_fileals Eingabe eingegeben hätte.

Das ist jedoch umständlich, daher haben sie Pfeifen hergestellt, um das einfacher zu machen. thing1 | thing2macht das gleiche wiething1 > temp_file && thing2 < temp_file

BEARBEITEN, um weitere Details zur Frage im Kommentar bereitzustellen:

Wenn >versucht wird, sowohl "an Programm übergeben" als auch "in Datei schreiben" zu sein, kann dies zu Problemen in beide Richtungen führen.

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

Zweites Beispiel: Wie Florian Diesch betonte, was ist, wenn es an anderer Stelle im System einen anderen Befehl mit demselben Namen gibt (der sich im Ausführungspfad befindet). Wenn Sie beabsichtigen, eine Datei mit diesem Namen in Ihrem aktuellen Ordner zu erstellen, stecken Sie fest.

Drittens: Wenn Sie einen Befehl falsch eingeben, werden Sie nicht gewarnt, dass der Befehl nicht vorhanden ist. Wenn Sie jetzt tippen, werden Sie ls | gerp log.txtdarüber informiert bash: gerp: command not found. Wenn >beides gemeint ist, erstellt es einfach eine neue Datei für Sie (und warnt Sie, dass es nicht weiß, was es tun soll log.txt).

David Oneill
quelle
Danke. Sie haben erwähnt thing1 > temp_file && thing2 < temp_file, mit Rohren einfacher umzugehen. Aber warum nicht den >Operator wiederverwenden , um dies zu tun, z. B. thing1 > thing2für Befehle thing1und thing2? Warum ein zusätzlicher Betreiber |?
John Threepwood
1
"Nehmen Sie die Ausgabe und schreiben Sie sie in eine Datei" ist eine andere Aktion als "Nehmen Sie die Ausgabe und übergeben Sie sie an ein anderes Programm". Ich werde mehr Gedanken in meine Antwort bearbeiten ...
David Oneill
1
@ JohnThreepwood Sie haben unterschiedliche Bedeutungen. Was ist, wenn ich etwas in eine Datei mit dem Namen umleiten möchte less? thing | lessund thing > lesssind 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" nur syntaktischer Zucker für "thing1 | tee temp_file" ist? Seit ich etwas über tee erfahre, verwende ich fast nie mehr Weiterleitungen.
Sridhar Sarnobat
2
@ Sridhar-Sarnobat nein, der teeBefehl macht etwas anderes. teeSchreibt die Ausgabe sowohl auf den Bildschirm ( stdout) als auch in die Datei. Redirect macht nur die Datei.
David Oneill
22

Wenn die Bedeutung von foo > bardavon abhängt, ob es einen Befehl mit dem Namen gibt bar, der die Verwendung der Umleitung erheblich erschwert und fehleranfälliger macht: Jedes Mal, wenn ich zu einer Datei umleiten möchte, musste ich zuerst überprüfen, ob es einen Befehl mit dem Namen meiner Zieldatei gibt.

Florian Diesch
quelle
Dies ist nur dann ein Problem, wenn Sie barin ein Verzeichnis schreiben , das Teil Ihrer $PATHenv-Variablen ist. Wenn Sie sich in etwas wie / bin befinden, könnte dies kein Problem sein. Aber selbst dann barmüsste die Berechtigung für ausführbare Dateien gesetzt sein, damit die Shell nicht nur nach ausführbaren Dateien sucht, barsondern diese auch ausführen kann. Wenn es um das Überschreiben vorhandener Dateien geht, noclobersollte die Shell-Option das Überschreiben vorhandener Dateien in Umleitungen verhindern.
Sergiy Kolodyazhnyy
13

Aus dem Unix- und Linux-Systemverwaltungshandbuch:

Umleitung

Die Shell interpretiert die Symbole <,> und >> als Anweisungen, um die Eingabe oder Ausgabe eines Befehls in oder aus einer Datei umzuleiten .

Rohre

Verwenden Sie |, um den STDOUT eines Befehls mit dem STDIN eines anderen Befehls zu verbinden Symbol, gemeinhin als Pfeife bekannt.

Meine Interpretation lautet also: Wenn es Befehl für Befehl ist, benutze eine Pipe. Wenn Sie in oder aus einer Datei ausgeben, verwenden Sie die Umleitung.

Mr Was auch immer
quelle
12

Es gibt einen entscheidenden Unterschied zwischen den beiden Operatoren:

  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 Befehls ls an grep mithilfe von pipe ( |), und der Befehl grep sucht nach file.txt in der Eingabe, die ihm vom vorherigen Befehl bereitgestellt wurde.

Wenn Sie dieselbe Aufgabe im ersten Szenario ausführen müssten, wäre dies:

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

So wird eine Pipe (mit |) verwendet, um die Ausgabe an einen anderen Befehl zu senden, wohingegen eine Umleitung (mit >) verwendet wird, um die Ausgabe an eine Datei umzuleiten.

Ankit
quelle
3

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

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

Sie können wie folgt von Umleitungen denken: cat [<infile] [>outfile]. Dies impliziert, dass die Reihenfolge keine Rolle spielt: cat <infile >outfileist dasselbe wie cat >outfile <infile. Sie können sogar Weiterleitungen mit anderen Argumenten verwechseln: cat >outfile <infile -bund cat <infile -b >outfilebeides ist vollkommen in Ordnung. Sie können auch String zusammen mehr als ein Eingang oder Ausgang (Eingänge werden nacheinander gelesen werden und alle Ausgaben werden jede Ausgabedatei geschrieben werden): cat >outfile1 >outfile2 <infile1 <infile2. Das Ziel oder die Quelle einer Umleitung kann entweder ein Dateiname oder der Name eines Streams sein (wie & 1, zumindest in Bash).

Pipes trennen jedoch einen Befehl vollständig von einem anderen. 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. Zum Beispiel:

cat <infile >outfile | cat <infile2 >outfile2

Die erste catliest die Zeilen aus der Infile-Datei, schreibt dann jede Zeile gleichzeitig in die Outfile-Datei und sendet sie an die zweite cat.

In der zweiten catZeile liest die Standardeingabe zuerst aus der Pipe (den Inhalt von infile), dann aus infile2 und schreibt jede Zeile in outfile2. Nachdem dies ausgeführt wurde, ist outfile eine Kopie von infile, und outfile2 enthält infile, gefolgt von infile2.

Schließlich machen Sie mit der "here string" -Umleitung (nur Bash-Familie) und Backticks etwas wirklich Ähnliches wie in Ihrem Beispiel:

grep blah <<<`ls`

ergibt das gleiche Ergebnis wie

ls | grep blah

Ich denke jedoch, dass die Umleitungsversion zuerst die gesamte Ausgabe von ls in einen Puffer (im Speicher) liest und diesen Puffer dann zeilenweise an grep weiterleitet, während die Pipe-Version jede Zeile von ls entnimmt, sobald sie auftaucht. und übergebe diese Zeile an grep.

user319857
quelle
1
Nitpick: Reihenfolge spielt bei der Umleitung eine Rolle, wenn Sie eine fd zu einer anderen echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blahumleiten : Bei der Umleitung zu einer Datei wird nur die letzte Umleitung verwendet. echo yes >/tmp/blah >/tmp/blah2werde nur schreiben /tmp/blah2.
muru
2
Redirect ist eigentlich kein Argument für das Programm. Dem Programm ist es egal, wohin die Ausgabe geht (oder von wo die Eingabe kommt). Es ist nur eine Art, Bash zu erklären, wie die Dinge angeordnet werden, bevor das Programm ausgeführt wird.
Alois Mahdal
3

Hinweis: Die Antwort spiegelt mein eigenes Verständnis dieser Mechanismen wider, das sich aus der Recherche und dem Lesen der Antworten der Kollegen auf dieser Site und unter unix.stackexchange.com ergibt . Sie wird im Laufe der Zeit aktualisiert. Zögern Sie nicht, Fragen zu stellen oder Verbesserungen in den Kommentaren vorzuschlagen. Ich schlage auch vor, Sie versuchen zu sehen, wie Syscalls in Shell mit straceBefehl funktionieren . Lassen Sie sich auch nicht von der Vorstellung von Internals oder Syscalls einschüchtern - Sie müssen sie nicht kennen oder verwenden können, um zu verstehen, wie Shell Dinge tut, aber sie helfen auf jeden Fall, sie zu verstehen.

TL; DR

  • |Pipes sind keinem Eintrag auf der Festplatte zugeordnet, haben also keine Inode- Nummer des Festplatten-Dateisystems (haben aber Inode im virtuellen Pipef- Dateisystem im Kernel-Space), aber bei Umleitungen handelt es sich häufig um Dateien, die Festplatteneinträge haben und daher entsprechende inode.
  • Pipes sind nicht in lseek()der Lage, sodass Befehle einige Daten nicht lesen und dann zurückspulen können. Wenn Sie jedoch mit einer Datei umleiten >oder diese <normalerweise als lseek()Objekt verwenden, können Befehle nach Belieben navigieren.
  • Weiterleitungen sind Manipulationen an Dateideskriptoren, die vielfältig sein können. Pipes haben nur zwei Dateideskriptoren - einen für den linken und einen für den rechten Befehl
  • Die Umleitung auf Standard-Streams und Pipes wird gepuffert.
  • Bei Rohren handelt es sich fast immer um Gabeln, und daher handelt es sich um Prozesspaare. Umleitungen - nicht immer, obwohl in beiden Fällen resultierende Dateideskriptoren von Unterprozessen geerbt werden.
  • Pipes verbinden immer Dateideskriptoren (ein Paar) und Umleitungen - entweder mit einem Pfadnamen oder mit Dateideskriptoren.
  • Pipes sind Inter-Process-Kommunikationsmethoden, während Umleitungen nur Manipulationen an geöffneten Dateien oder dateiähnlichen Objekten sind
  • Beide verwenden dup2()Systemaufrufe unter der Haube, um Kopien von Dateideskriptoren bereitzustellen, bei denen der tatsächliche Datenfluss stattfindet.
  • Umleitungen können mit dem execeingebauten Befehl "global" angewendet werden (siehe dies und das ). Wenn Sie dies also tun, wird exec > output.txtjeder Befehl output.txtvon da an beschrieben. |Pipes werden nur für den aktuellen Befehl angewendet (dh entweder einfache Befehle oder untergeordnete ähnliche seq 5 | (head -n1; head -n2)oder zusammengesetzte Befehle).
  • Wenn die Umleitung für Dateien durchgeführt wird, verwenden Dinge wie echo "TEST" > fileund echo "TEST" >> filebeide open()syscall für diese Datei ( siehe auch ) und rufen den Dateideskriptor ab, um ihn an diese Datei weiterzuleiten dup2(). Pipes verwenden |nur pipe()und dup2()Syscall.

  • Bei der Ausführung von Befehlen handelt es sich bei Pipes und Umleitungen nur um Dateideskriptoren - dateiähnliche Objekte, in die sie möglicherweise blind schreiben oder intern bearbeiten (was zu unerwartetem Verhalten führen kann, aptz. B. dazu führt, dass sie nicht einmal in die Standardausgabe schreiben wenn es weiß, dass es eine Umleitung gibt).

Einführung

Um zu verstehen, wie sich diese beiden Mechanismen unterscheiden, müssen ihre wesentlichen Eigenschaften, die Geschichte der beiden und ihre Wurzeln in der Programmiersprache C bekannt sein. In der Tat, zu wissen , welche Datei - Deskriptoren sind und wie dup2()und pipe()Systemaufrufe Arbeit ist wichtig, sowie lseek(). Shell ist dazu gedacht, diese Mechanismen für den Benutzer abstrakt zu machen. Wenn Sie jedoch tiefer als die Abstraktion graben, können Sie die wahre Natur des Verhaltens von Shell besser verstehen.

Die Ursprünge von Umleitungen und Pipes

Laut Dennis Ritches Artikel Prophetic Petroglyphs ( Prophetische Petroglyphen) stammten Pfeifen aus einem internen Memo von Malcolm Douglas McIlroy aus dem Jahr 1964 , als sie am Betriebssystem Multics arbeiteten . Zitat:

Um meine größten Sorgen auf den Punkt zu bringen:

  1. Wir sollten einige Möglichkeiten haben, Programme wie Gartenschlauch anzuschließen - schrauben Sie in einem anderen Segment, wenn es notwendig wird, Daten auf andere Weise zu massieren. Dies ist auch der Weg von IO.

Was offensichtlich war, war, dass Programme zu der Zeit in der Lage waren, auf die Festplatte zu schreiben, was jedoch ineffizient war, wenn die Ausgabe groß war. So zitieren Sie Brian Kernighans Erklärung im Unix-Pipeline- Video:

Erstens müssen Sie kein einziges großes umfangreiches Programm schreiben - es gibt bereits kleinere Programme, die möglicherweise bereits Teile der Arbeit erledigen ... Zum anderen ist es möglich, dass die von Ihnen verarbeitete Datenmenge nicht passt, wenn Sie haben es in einer Datei gespeichert ... denn denken Sie daran, wir sind in den Tagen zurück, in denen die Festplatten auf diesen Dingen, wenn Sie Glück hatten, ein oder zwei Megabyte Daten hatten ... Die Pipeline musste also nie die gesamte Ausgabe instanziieren .

Der konzeptionelle Unterschied ist also offensichtlich: Pipes sind ein Mechanismus, mit dem Programme miteinander kommunizieren. Weiterleitungen - sind das Schreiben in eine Datei auf der Basisebene. In beiden Fällen macht Shell diese beiden Dinge einfach, aber unter der Motorhaube ist eine Menge los.

Tiefer gehen: Systemaufrufe und interne Funktionsweise der Shell

Wir beginnen mit dem Begriff des Dateideskriptors . Dateideskriptoren beschreiben im Grunde genommen eine geöffnete Datei (unabhängig davon, 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 beiden Standarddatenströme (stdin, stdout, stderr) sind Dateideskriptoren 0,1 bzw. 2. Woher kommen sie ? Nun, in Shell-Befehlen werden die Dateideskriptoren von ihrer übergeordneten Shell geerbt. Und dies gilt im Allgemeinen für alle Prozesse. Der untergeordnete Prozess erbt die Dateideskriptoren der übergeordneten Prozesse. Für Daemons ist es üblich, alle geerbten Dateideskriptoren zu schließen und / oder an andere Stellen umzuleiten.

Zurück zur Umleitung. Was ist das eigentlich? Es ist ein Mechanismus, der die Shell anweist, Dateideskriptoren für den Befehl vorzubereiten (da Umleitungen von der Shell vorgenommen werden, bevor der Befehl ausgeführt wird) und sie an die vom Benutzer vorgeschlagene Stelle zu setzen. Die Standarddefinition für die Ausgabeumleitung lautet

[n]>word

Dass [n]es die Dateideskriptornummer gibt. Wenn Sie dies tun, wird dort echo "Something" > /dev/nulldie Zahl 1 impliziert, und echo 2> /dev/null.

Unter der Haube geschieht dies durch Duplizieren des Dateideskriptors per dup2()Systemaufruf. Lass uns nehmen df > /dev/null. Die Shell erstellt einen untergeordneten Prozess df, der ausgeführt wird. Vorher wird er jedoch /dev/nullals Dateideskriptor 3 geöffnet und dup2(3,1)ausgegeben, wodurch eine Kopie von Dateideskriptor 3 erstellt wird. Die Kopie lautet 1. Sie wissen, wie Sie zwei Dateien file1.txtund haben file2.txt, und wenn Sie dies tun cp file1.txt file2.txt, haben Sie zwei gleiche Dateien, aber Sie können sie unabhängig voneinander bearbeiten? Das ist irgendwie das Gleiche, was hier passiert. Oft können Sie vor dem Ausführen sehen , dass das bashtun wird dup(1,10)eine Kopie Dateideskriptor # 1 zu machen , das ist stdout(und diese Kopie wird fd # 10 sein) um sie später wieder herstellen. Wichtig ist zu beachten, dass wenn Sie eingebaute Befehle berücksichtigen(die Teil der Shell selbst sind und keine Datei in /binoder an anderer Stelle haben) oder einfache Befehle in der nicht interaktiven Shell , erstellt die Shell keinen untergeordneten Prozess.

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

Eines der wichtigsten Dinge, die bei der Umleitung beachtet werden müssen, ist, dass ihre Reihenfolge nicht festgelegt ist, sondern entscheidend dafür, wie die Shell interpretiert, was der Benutzer möchte. 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 vielfältig sein:

und viele weitere.

Sanitär mit pipe()unddup2()

Wie entstehen Rohre? Über pipe()syscall , das als Eingabe ein Array (auch als Liste bezeichnet) verwendet, das pipefdaus zwei Elementen des Typs int(Ganzzahl) besteht. Diese beiden Ganzzahlen sind Dateideskriptoren. Das pipefd[0]wird das Leseende der Pipe und pipefd[1]das Schreibende sein. Also df | grep 'foo', greperhält Kopie pipefd[0]und dfeine Kopie erhalten pipefd[1]. Aber wie ? Natürlich mit der Magie von dup2()Syscall. Nehmen dfwir in unserem Beispiel an, wir pipefd[1]haben # 4, damit die Shell ein untergeordnetes Element erzeugt, das Sie ausführen dup2(4,1)(erinnern Sie sich an mein cpBeispiel?) Und dann execve()ausführen, um tatsächlich zu laufen df. Natürlich,dferbt den Dateideskriptor Nr. 1, merkt jedoch nicht, dass er nicht mehr auf das Terminal zeigt, sondern auf fd Nr. 4, das eigentlich das Schreibende der Pipe ist. Natürlich wird dasselbe mit grep 'foo'Ausnahme einer unterschiedlichen Anzahl von Dateideskriptoren auftreten.

Nun eine interessante Frage: Können wir Pipes herstellen, die auch fd # 2 umleiten, nicht nur fd # 1? Ja, |&genau das macht man in der Bash. Der POSIX - Standard erfordert Shell Befehlssprache zur Unterstützung der df 2>&1 | grep 'foo'Syntax für diesen Zweck, aber bashtut |&auch.

Zu beachten ist, dass Pipes immer mit Dateideskriptoren arbeiten. Es existiert FIFOoder Named Pipe , die einen Dateinamen auf der Festplatte hat und wir Sie als Datei verwenden, sondern verhält sich wie ein Rohr. Bei den |Pipetypen handelt es sich jedoch um so genannte anonyme Pipes. Sie haben keinen Dateinamen, da sie eigentlich nur zwei miteinander verbundene Objekte sind. Die Tatsache, dass es sich nicht um Dateien handelt, ist auch eine wichtige Implikation: Pipes sind nicht in lseek()der Lage. Dateien, entweder im Speicher oder auf der Festplatte, sind statisch - Programme können lseek()syscall verwenden, um zu Byte 120 zu springen, dann zurück zu Byte 10 und bis zum Ende weiterzuleiten. Pipes sind nicht statisch - sie sind sequentiell und daher können Sie keine Daten zurückspulen, die Sie von ihnen erhaltenlseek(). Dies ist es, was einige Programme darauf aufmerksam macht, ob sie aus einer Datei oder einer Pipe lesen, und somit die notwendigen Anpassungen für eine effiziente Leistung vornehmen können. Mit anderen Worten, ein progkann erkennen, ob ich cat file.txt | progoder prog < input.txt. Ein echtes Arbeitsbeispiel dafür ist der Schwanz .

Die anderen beiden sehr interessanten Eigenschaften von Pipes sind, dass sie einen Puffer haben, der unter Linux 4096 Bytes beträgt , und dass sie tatsächlich ein Dateisystem haben, wie es im Linux-Quellcode definiert ist ! Sie sind nicht nur ein Objekt zum Weitergeben von Daten, sie sind selbst eine Datenstruktur! Da es ein Pipefs-Dateisystem gibt, das sowohl Pipes als auch FIFOs verwaltet, haben Pipes eine Inode- Nummer in 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 wie Umleitungen unidirektional. Bei einigen Unix-ähnlichen Implementierungen gibt es bidirektionale Pipes. Mit der Magie des Shell-Skripts können Sie auch unter Linux bidirektionale Pipes erstellen .

Siehe auch:

Sergiy Kolodyazhnyy
quelle
2

Um die anderen Antworten zu ergänzen, gibt es auch subtile semantische Unterschiede - z. B. werden Pipes leichter geschlossen als umgeleitet:

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

Wenn im ersten Beispiel der erste Aufruf headbeendet wird, wird die Pipe geschlossen und seqbeendet, sodass für den zweiten keine Eingabe verfügbar ist head.

Im zweiten Beispiel belegt head die erste Zeile, aber wenn es seine eigene stdin Pipe schließt , bleibt die Datei für den nächsten Aufruf geöffnet.

Das dritte Beispiel zeigt, dass, wenn wir readdas Schließen der Pipe vermeiden, diese weiterhin im Unterprozess verfügbar ist.

Der "Stream" ist also die Sache, durch die wir Daten leiten (stdin usw.), und ist in beiden Fällen die gleiche, aber die Pipe verbindet Streams von zwei Prozessen, wobei eine Umleitung einen Stream zwischen einem Prozess und einer Datei verbindet, also Sie kann die Quelle sowohl der Ähnlichkeiten als auch der Unterschiede sehen.

PS: Wenn Sie genauso neugierig und / oder überrascht sind wie ich, können Sie sich weiter damit befassen, um trapzu sehen, wie sich die Prozesse auflösen, z. B .:

(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 wird der erste Prozess vor dem 1Drucken geschlossen, manchmal danach.

Ich fand es auch interessant, exec <&-den Stream von der Umleitung zu schließen, um das Verhalten der Pipe zu approximieren (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`
Julian de Bhal
quelle
"Wenn der erste Aufruf von head beendet wird, wird die Pipe geschlossen." Dies ist aus zwei Gründen ungenau. Eins, (head -n1; head -n1), ist eine Subshell mit zwei Befehlen, von denen jeder das gelesene Ende der Pipe als Deskriptor 0 erbt, und daher hat die Subshell UND jeder Befehl diesen Dateideskriptor geöffnet. Der zweite Grund ist, dass Sie das mit strace -f bash -c 'seq 5 | sehen können (Kopf -n1; Kopf -n1) '. Also schließt der erste Kopf nur seine Kopie des Dateideskriptors
Sergiy Kolodyazhnyy
Das dritte Beispiel ist ebenfalls ungenau, da readnur die erste Zeile belegt wird (das ist ein Byte für eine 1neue Zeile). seqgesendet insgesamt 10 Bytes (5 Zahlen und 5 Zeilenvorschübe). Es sind also noch 8 Bytes im Pipe-Buffer, und deshalb headfunktioniert second - es sind noch Daten im Pipe-Buffer verfügbar. Übrigens, der Kopf geht nur, wenn 0 Bytes gelesen werden, ein bisschen wie inhead /dev/null
Sergiy Kolodyazhnyy
Danke für die Klarstellung. Verstehe ich richtig, dass seq 5 | (head -n1; head -n1)beim ersten Aufruf die Pipe geleert wird, also es noch im geöffneten Zustand aber keine Daten für den zweiten Aufruf gibt head? Der Unterschied im Verhalten zwischen der Pipe und der Umleitung besteht also darin, dass head alle Daten aus der Pipe zieht, aber nur die zwei Zeilen aus dem Datei-Handle?
Julian de Bhal
Das ist richtig. Und es ist etwas, was man an dem straceBefehl sehen kann, den ich im ersten Kommentar gegeben habe. Bei der Umleitung befindet sich die tmp-Datei auf der Festplatte, wodurch sie durchsucht werden kann (da sie syscall verwenden lseek()- Befehle können vom ersten bis zum letzten Byte in der Datei springen, wie sie möchten. Pipes sind jedoch sequentiell und nicht durchsuchbar. Die einzige Möglichkeit für den Kopf, dies zu tun Aufgabe ist es, zuerst alles zu lesen, oder wenn die Datei groß ist - ordnen Sie einen Teil davon per mmap()Anruf dem RAM zu . Ich habe es einmal tailin Python selbst gemacht und bin auf genau dasselbe Problem
gestoßen
Es ist auch wichtig zu bedenken, dass (...)das Leseende der Pipe (Dateideskriptor) zuerst der Subshell zugewiesen wird und die Subshell für jeden Befehl eine Kopie ihres eigenen Standardnamens erstellt (...). Sie werden also technisch aus demselben Objekt gelesen. Zuerst head denkt , dass es aus seiner eigenen stdin zu lesen. Zweitens headdenkt, es hat einen eigenen Standard. Aber in Wirklichkeit ist ihre fd # 1 (stdin) nur eine Kopie derselben fd, die das Ende der Pipe liest. Außerdem habe ich eine Antwort gepostet. Vielleicht hilft das, die Dinge zu klären.
Sergiy Kolodyazhnyy
1

Ich bin heute in C auf ein Problem gestoßen. Grundsätzlich haben Pipes auch andere Semantiken, um sie umzuleiten, selbst wenn sie an gesendet werden stdin. Wirklich denke ich, angesichts der Unterschiede, sollten Pipes irgendwo anders als hingehen stdin, damit stdinund lassen Sie es stdpipe(um ein beliebiges Differential zu machen) auf verschiedene Arten behandelt werden können.

Bedenken Sie. Bei der Weiterleitung eines Programms an ein anderes fstatscheint die Ausgabe Null zu ergeben, st_sizeobwohl ls -lha /proc/{PID}/fdangezeigt wird, dass eine Datei vorhanden ist. Wenn Sie eine Datei umleiten ist dies nicht der Fall (zumindest auf debian wheezy, stretchund jessieVanille und ubuntu 14.04, 16.04Vanille.

Wenn Sie cat /proc/{PID}/fd/0eine Weiterleitung haben, können Sie die Lesung so oft wiederholen, wie Sie möchten. Wenn Sie dies mit einer Pipe tun, werden Sie feststellen, dass Sie beim zweiten Ausführen der Task nicht die gleiche Ausgabe erhalten.

MrMesees
quelle