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 > thing2
vsthing1 | thing2
thing1 > thing2
- Ihre Shell wird das genannte Programm ausführen
thing1
- Alles, was
thing1
ausgegeben wird, wird in einer Datei namens platziert thing2
. (Hinweis - falls thing2
vorhanden, wird es überschrieben)
Wenn Sie die Ausgabe von program thing1
an ein Programm übergeben möchten, das aufgerufen wird thing2
, können Sie Folgendes tun:
thing1 > temp_file && thing2 < temp_file
was würde
- Führen Sie das genannte Programm aus
thing1
- Speichern Sie die Ausgabe in einer Datei mit dem Namen
temp_file
- Führen Sie das Programm mit dem Namen aus
thing2
und tun Sie so, als ob die Person an der Tastatur den Inhalt temp_file
als Eingabe eingegeben hätte.
Das ist jedoch umständlich, daher haben sie Pfeifen hergestellt, um das einfacher zu machen. thing1 | thing2
macht 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.txt
darü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
).
thing1 > temp_file && thing2 < temp_file
, mit Rohren einfacher umzugehen. Aber warum nicht den>
Operator wiederverwenden , um dies zu tun, z. B.thing1 > thing2
für Befehlething1
undthing2
? Warum ein zusätzlicher Betreiber|
?less
?thing | less
undthing > less
sind vollkommen verschieden, da sie verschiedene Dinge tun. Was Sie vorschlagen, würde eine Mehrdeutigkeit schaffen.tee
Befehl macht etwas anderes.tee
Schreibt die Ausgabe sowohl auf den Bildschirm (stdout
) als auch in die Datei. Redirect macht nur die Datei.Wenn die Bedeutung von
foo > bar
davon abhängt, ob es einen Befehl mit dem Namen gibtbar
, 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.quelle
bar
in ein Verzeichnis schreiben , das Teil Ihrer$PATH
env-Variablen ist. Wenn Sie sich in etwas wie / bin befinden, könnte dies kein Problem sein. Aber selbst dannbar
müsste die Berechtigung für ausführbare Dateien gesetzt sein, damit die Shell nicht nur nach ausführbaren Dateien sucht,bar
sondern diese auch ausführen kann. Wenn es um das Überschreiben vorhandener Dateien geht,noclober
sollte die Shell-Option das Überschreiben vorhandener Dateien in Umleitungen verhindern.Aus dem Unix- und Linux-Systemverwaltungshandbuch:
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.
quelle
Es gibt einen entscheidenden Unterschied zwischen den beiden Operatoren:
ls > log.txt
-> Dieser Befehl sendet die Ausgabe an die Datei log.txt.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:
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.quelle
Es gibt einen großen syntaktischen Unterschied zwischen den beiden:
Sie können wie folgt von Umleitungen denken:
cat [<infile] [>outfile]
. Dies impliziert, dass die Reihenfolge keine Rolle spielt:cat <infile >outfile
ist dasselbe wiecat >outfile <infile
. Sie können sogar Weiterleitungen mit anderen Argumenten verwechseln:cat >outfile <infile -b
undcat <infile -b >outfile
beides 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:
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:
Die erste
cat
liest die Zeilen aus der Infile-Datei, schreibt dann jede Zeile gleichzeitig in die Outfile-Datei und sendet sie an die zweitecat
.In der zweiten
cat
Zeile 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:
ergibt das gleiche Ergebnis wie
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.
quelle
echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah
umleiten : Bei der Umleitung zu einer Datei wird nur die letzte Umleitung verwendet.echo yes >/tmp/blah >/tmp/blah2
werde nur schreiben/tmp/blah2
.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
strace
Befehl 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.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 alslseek()
Objekt verwenden, können Befehle nach Belieben navigieren.dup2()
Systemaufrufe unter der Haube, um Kopien von Dateideskriptoren bereitzustellen, bei denen der tatsächliche Datenfluss stattfindet.exec
eingebauten Befehl "global" angewendet werden (siehe dies und das ). Wenn Sie dies also tun, wirdexec > output.txt
jeder Befehloutput.txt
von da an beschrieben.|
Pipes werden nur für den aktuellen Befehl angewendet (dh entweder einfache Befehle oder untergeordnete ähnlicheseq 5 | (head -n1; head -n2)
oder zusammengesetzte Befehle).Wenn die Umleitung für Dateien durchgeführt wird, verwenden Dinge wie
echo "TEST" > file
undecho "TEST" >> file
beideopen()
syscall für diese Datei ( siehe auch ) und rufen den Dateideskriptor ab, um ihn an diese Datei weiterzuleitendup2()
. Pipes verwenden|
nurpipe()
unddup2()
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,
apt
z. 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()
undpipe()
Systemaufrufe Arbeit ist wichtig, sowielseek()
. 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:
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:
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
Dass
[n]
es die Dateideskriptornummer gibt. Wenn Sie dies tun, wird dortecho "Something" > /dev/null
die Zahl 1 impliziert, undecho 2> /dev/null
.Unter der Haube geschieht dies durch Duplizieren des Dateideskriptors per
dup2()
Systemaufruf. Lass uns nehmendf > /dev/null
. Die Shell erstellt einen untergeordneten Prozessdf
, der ausgeführt wird. Vorher wird er jedoch/dev/null
als Dateideskriptor 3 geöffnet unddup2(3,1)
ausgegeben, wodurch eine Kopie von Dateideskriptor 3 erstellt wird. Die Kopie lautet 1. Sie wissen, wie Sie zwei Dateienfile1.txt
und habenfile2.txt
, und wenn Sie dies tuncp 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 dasbash
tun wirddup(1,10)
eine Kopie Dateideskriptor # 1 zu machen , das iststdout
(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/bin
oder 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 wiedup2()
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:
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, daspipefd
aus zwei Elementen des Typsint
(Ganzzahl) besteht. Diese beiden Ganzzahlen sind Dateideskriptoren. Daspipefd[0]
wird das Leseende der Pipe undpipefd[1]
das Schreibende sein. Alsodf | grep 'foo'
,grep
erhält Kopiepipefd[0]
unddf
eine Kopie erhaltenpipefd[1]
. Aber wie ? Natürlich mit der Magie vondup2()
Syscall. Nehmendf
wir in unserem Beispiel an, wirpipefd[1]
haben # 4, damit die Shell ein untergeordnetes Element erzeugt, das Sie ausführendup2(4,1)
(erinnern Sie sich an meincp
Beispiel?) Und dannexecve()
ausführen, um tatsächlich zu laufendf
. Natürlich,df
erbt 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 mitgrep '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 derdf 2>&1 | grep 'foo'
Syntax für diesen Zweck, aberbash
tut|&
auch.Zu beachten ist, dass Pipes immer mit Dateideskriptoren arbeiten. Es existiert
FIFO
oder 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 inlseek()
der Lage. Dateien, entweder im Speicher oder auf der Festplatte, sind statisch - Programme könnenlseek()
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, einprog
kann erkennen, ob ichcat file.txt | prog
oderprog < 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:
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:
pipe()
syscall und erstellt wirddup2()
.<<
,<<<
werden als anonyme (nicht verknüpfte) temporäre Dateien inbash
und implementiertksh
, während< <()
anonyme Pipes verwendet werden./bin/dash
verwendet Rohre für<<
. Siehe Was ist der Unterschied zwischen <<, <<< und <<in bash?quelle
Um die anderen Antworten zu ergänzen, gibt es auch subtile semantische Unterschiede - z. B. werden Pipes leichter geschlossen als umgeleitet:
Wenn im ersten Beispiel der erste Aufruf
head
beendet wird, wird die Pipe geschlossen undseq
beendet, sodass für den zweiten keine Eingabe verfügbar isthead
.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
read
das 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
trap
zu sehen, wie sich die Prozesse auflösen, z. B .:Manchmal wird der erste Prozess vor dem
1
Drucken 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):quelle
read
nur die erste Zeile belegt wird (das ist ein Byte für eine1
neue Zeile).seq
gesendet insgesamt 10 Bytes (5 Zahlen und 5 Zeilenvorschübe). Es sind also noch 8 Bytes im Pipe-Buffer, und deshalbhead
funktioniert 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
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 gibthead
? 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?strace
Befehl 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 verwendenlseek()
- 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 permmap()
Anruf dem RAM zu . Ich habe es einmaltail
in Python selbst gemacht und bin auf genau dasselbe Problem(...)
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. Zuersthead
denkt , dass es aus seiner eigenen stdin zu lesen. Zweitenshead
denkt, 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.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 hingehenstdin
, damitstdin
und lassen Sie esstdpipe
(um ein beliebiges Differential zu machen) auf verschiedene Arten behandelt werden können.Bedenken Sie. Bei der Weiterleitung eines Programms an ein anderes
fstat
scheint die Ausgabe Null zu ergeben,st_size
obwohlls -lha /proc/{PID}/fd
angezeigt wird, dass eine Datei vorhanden ist. Wenn Sie eine Datei umleiten ist dies nicht der Fall (zumindest auf debianwheezy
,stretch
undjessie
Vanille und ubuntu14.04
,16.04
Vanille.Wenn Sie
cat /proc/{PID}/fd/0
eine 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.quelle