it-swarm.com.de

Prozesssubstitution und Rohrleitung

Ich habe mich gefragt, wie ich Folgendes verstehen soll:

Es ist eine mächtige Technik, die Standardausgabe eines Befehls in die Standardausgabe eines anderen Befehls zu leiten. Aber was ist, wenn Sie die Standardausgabe mehrerer Befehle weiterleiten müssen? Hier kommt die Prozesssubstitution ins Spiel.

Mit anderen Worten, kann die Prozesssubstitution das tun, was die Pipe kann?

Was kann die Prozesssubstitution tun, die Pipe jedoch nicht?

89
Tim

Eine gute Möglichkeit, den Unterschied zwischen ihnen zu erkennen, besteht darin, ein wenig in der Befehlszeile zu experimentieren. Trotz der visuellen Ähnlichkeit bei der Verwendung des < Zeichen, es macht etwas ganz anderes als eine Weiterleitung oder Pipe.

Verwenden wir zum Testen den Befehl date.

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

Dies ist ein sinnloses Beispiel, aber es zeigt, dass cat die Ausgabe von date auf STDIN akzeptiert und wieder ausgespuckt hat. Die gleichen Ergebnisse können durch Prozesssubstitution erzielt werden:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

Was jedoch gerade hinter den Kulissen passierte, war anders. Anstatt einen STDIN-Stream zu erhalten, wurde cat tatsächlich der Name einer Datei übergeben, die zum Öffnen und Lesen benötigt wurde. Sie können diesen Schritt sehen, indem Sie echo anstelle von cat verwenden.

$ echo <(date)
/proc/self/fd/11

Als cat den Dateinamen erhielt, las sie den Inhalt der Datei für uns. Auf der anderen Seite zeigte uns echo nur den Namen der Datei, die übergeben wurde. Dieser Unterschied wird deutlicher, wenn Sie weitere Substitutionen hinzufügen:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

Es ist möglich, die Prozessersetzung (die eine Datei generiert) und die Eingabeumleitung (die eine Datei mit STDIN verbindet) zu kombinieren:

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

Es sieht ziemlich gleich aus, aber diesmal wurde der Katze der STDIN-Stream anstelle eines Dateinamens übergeben. Sie können dies sehen, indem Sie es mit echo versuchen:

$ echo < <(date)
<blank>

Da echo STDIN nicht liest und kein Argument übergeben wurde, erhalten wir nichts.

Pipes und Input-Weiterleitungen verschieben Inhalte in den STDIN-Stream. Die Prozessersetzung führt die Befehle aus, speichert ihre Ausgabe in einer speziellen temporären Datei und übergibt diesen Dateinamen anstelle des Befehls. Welchen Befehl Sie auch verwenden, er wird als Dateiname behandelt. Beachten Sie, dass es sich bei der erstellten Datei nicht um eine reguläre Datei handelt, sondern um eine Named Pipe, die automatisch entfernt wird, sobald sie nicht mehr benötigt wird.

140
Caleb

Hier sind drei Dinge, die Sie mit der Prozessersetzung tun können, die sonst unmöglich sind.

Mehrere Prozesseingaben

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

Mit Rohren ist das einfach nicht möglich.

STDIN erhalten

Angenommen, Sie haben Folgendes:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

Und Sie möchten es direkt ausführen. Das Folgende schlägt kläglich fehl. Bash verwendet STDIN bereits zum Lesen des Skripts, sodass andere Eingaben nicht möglich sind.

curl -o - http://example.com/script.sh | bash 

Aber dieser Weg funktioniert perfekt.

bash <(curl -o - http://example.com/script.sh)

Ersetzung ausgehender Prozesse

Beachten Sie auch, dass die Prozessersetzung auch umgekehrt funktioniert. Sie können also so etwas tun:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

Das ist ein kompliziertes Beispiel, aber es sendet stdout an /dev/null, Während stderr an ein sed-Skript weitergeleitet wird, um die Namen der Dateien zu extrahieren, für die Ein Fehler "Berechtigung verweigert" wurde angezeigt und sendet diese Ergebnisse an eine Datei.

Beachten Sie, dass der erste Befehl und die Umleitung stdout in Klammern (nterschale) stehen, sodass nur das Ergebnis DIESES Befehls an /dev/null Gesendet wird und dies nicht der Fall ist. ' Leg dich nicht mit dem Rest der Leitung an.

26
tylerl

Ich sollte annehmen, dass Sie über bash oder eine andere erweiterte Shell sprechen, da die Posix-Shell nicht Prozessersetzung hat.

bash Handbuchseitenberichte:

Prozesssubstitution
Die Prozessersetzung wird auf Systemen unterstützt, die Named Pipes (FIFOs) oder die Methode/dev/fd zum Benennen geöffneter Dateien unterstützen. Es hat die Form <(Liste) oder> (Liste). Die Prozessliste wird ausgeführt, wobei ihre Eingabe oder Ausgabe mit einem FIFO oder einer Datei in/dev/fd) verbunden ist. Der Name dieser Datei wird als Ergebnis an als Argument an den aktuellen Befehl übergeben Die Erweiterung. Wenn das Formular> (Liste) verwendet wird, liefert das Schreiben in die Datei eine Eingabe für die Liste. Wenn das Formular <(Liste) verwendet wird, sollte die als Argument übergebene Datei gelesen werden, um die Ausgabe der Liste zu erhalten.

Wenn verfügbar, wird die Prozessersetzung gleichzeitig mit der Parameter- und Variablenerweiterung, der Befehlssubstitution und der arithmetischen Erweiterung durchgeführt.

Mit anderen Worten und aus praktischer Sicht können Sie einen Ausdruck wie den folgenden verwenden

<(commands)

als Dateiname für andere Befehle, für die eine Datei als Parameter erforderlich ist. Oder Sie können die Umleitung für eine solche Datei verwenden:

while read line; do something; done < <(commands)

Wenn ich noch einmal auf Ihre Frage zurückkehre, scheint es mir, dass Prozesssubstitution und Pipes nicht viel gemeinsam haben.

Wenn Sie die Ausgabe mehrerer Befehle nacheinander weiterleiten möchten, können Sie eine der folgenden Formen verwenden:

(command1; command2) | command3
{ command1; command2; } | command3

sie können aber auch die Umleitung für die Prozessersetzung verwenden

command3 < <(command1; command2)

schließlich, wenn command3 akzeptiert einen Dateiparameter (anstelle von stdin)

command3 <(command1; command2)
26
enzotib

Wenn ein Befehl eine Liste von Dateien als Argumente verwendet und diese Dateien als Eingabe (oder Ausgabe, aber nicht häufig) verarbeitet, kann jede dieser Dateien eine Named Pipe oder eine/dev/fd-Pseudodatei sein, die durch Prozesssubstitution transparent bereitgestellt wird:

$ sort -m <(command1) <(command2) <(command3)

Dadurch wird die Ausgabe der drei zu sortierenden Befehle "weitergeleitet", da sort eine Liste von Eingabedateien in der Befehlszeile enthalten kann.

10
camh

Es ist zu beachten, dass die Prozessersetzung nicht auf die Form <(command) beschränkt ist, die die Ausgabe von command als Datei verwendet. Es kann in der Form >(command) vorliegen, die eine Datei als Eingabe auch an command weiterleitet. Dies wird auch im Zitat des Bash-Handbuchs in der Antwort von @ enzotib erwähnt.

Für das obige Beispiel date | cat Wäre ein Befehl, der die Prozessersetzung der Form >(command) verwendet, um den gleichen Effekt zu erzielen,

date > >(cat)

Beachten Sie, dass > Vor >(cat) erforderlich ist. Dies kann wiederum durch echo wie in der Antwort von @ Caleb deutlich veranschaulicht werden.

$ echo >(cat)
/dev/fd/63

Ohne das zusätzliche > Wäre date >(cat) dasselbe wie date /dev/fd/63, Das eine Nachricht an stderr druckt.

Angenommen, Sie haben ein Programm, das nur Dateinamen als Parameter verwendet und stdin oder stdout nicht verarbeitet. Ich werde das stark vereinfachte Skript psub.sh Verwenden, um dies zu veranschaulichen. Der Inhalt von psub.sh Ist

#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"

Grundsätzlich wird getestet, ob beide Argumente Dateien sind (nicht unbedingt reguläre Dateien). Wenn dies der Fall ist, schreiben Sie das erste Feld jeder Zeile von "$1" Mit awk in "$2". Dann ist ein Befehl, der alles kombiniert, was bisher erwähnt wurde,

./psub.sh <(printf "a a\nc c\nb b") >(sort)

Dies wird gedruckt

a
b
c

und ist äquivalent zu

printf "a a\nc c\nb b" | awk '{print $1}' | sort

aber das Folgende wird nicht funktionieren, und wir müssen hier die Prozessersetzung verwenden.

printf "a a\nc c\nb b" | ./psub.sh | sort

oder seine äquivalente Form

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

Wenn ./psub.sh Außer den oben genannten auch stdin lautet, gibt es keine solche äquivalente Form, und in diesem Fall können wir nichts anstelle der Prozessersetzung verwenden (natürlich können Sie dies Verwenden Sie auch eine Named Pipe oder eine temporäre Datei, aber das ist eine andere Geschichte.

3
Weijun Zhou