Prozessersetzung und Pipe

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 alles tun, was Pipe kann?

Was kann eine Prozessersetzung tun, eine Pipe jedoch nicht?

Antwort

Ein guter Weg, um das Problem zu lösen Der Unterschied zwischen ihnen besteht darin, ein wenig in der Befehlszeile zu experimentieren. Trotz der visuellen Ähnlichkeit bei der Verwendung des Zeichens < wird etwas ganz anderes als eine Umleitung oder Pipe ausgeführt.

Verwenden wir die date Befehl zum Testen.

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

Dies ist ein sinnloses Beispiel, aber es zeigt, dass cat hat die Ausgabe von date auf STDIN akzeptiert und wieder ausgespuckt. Die gleichen Ergebnisse können durch Prozessersetzung 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 geöffnet werden musste und lesen Sie. 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 hat uns echo nur den Namen der Datei angezeigt, die übergeben wurde. Dieser Unterschied wird deutlicher, wenn Sie weitere Ersetzungen 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 

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

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

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

$ echo < <(date) <blank> 

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

Pipes und Eingabeumleitungen 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 die erstellte Datei keine reguläre Datei ist, sondern eine Named Pipe, die automatisch entfernt wird, sobald sie nicht mehr benötigt wird.

Kommentare

  • Wenn ich tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 besagt, dass durch die Prozessersetzung temporäre Dateien und keine benannten Pipes erstellt werden. Soweit ich weiß, erstellen Sie keine temporären Dateien. Das Schreiben in die Pipe beinhaltet niemals das Schreiben auf die Festplatte: stackoverflow.com/a/6977599/788700
  • Ich weiß, dass diese Antwort legitim ist ‚ weil das Wort grok verwendet wird: D
  • @Adobe Sie können bestätigen, ob die temporäre Dateisatzersetzung eine Named Pipe ist mit: [[ -p <(date) ]] && echo true. Dies erzeugt true, wenn ich es mit Bash 4.4 oder 3.2 ausführe.

Antwort

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) 

Dort Mit Pipes ist dies einfach nicht möglich.

Beibehalten von STDIN

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 

Diese Methode funktioniert jedoch einwandfrei.

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

Ausgehende Prozessersetzung

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 eine “ Berechtigung verweigert wurde “ Fehler wurde angezeigt und sendet dann DIESE Ergebnisse an eine Datei.

Beachten Sie, dass der erste Befehl und die stdout -Umleitung in Klammern stehen ( Unterschale ), sodass nur das Ergebnis DIESES Befehls an /dev/null gesendet wird und der Rest der Zeile nicht durcheinander gebracht wird.

Kommentare

  • Es ist ‚ erwähnenswert, dass in der diff Beispiel: Sie möchten sich vielleicht um den Fall kümmern, in dem cd möglicherweise fehlschlägt: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • “ beim Weiterleiten von stderr „: isn ‚ t the Zeigen Sie, dass dies kein Piping ist, sondern eine Fifo-Datei durchläuft?
  • @Gauthier no; Der Befehl wird nicht durch ein FIFO ersetzt, sondern durch einen Verweis auf den Dateideskriptor. “ echo < (echo) “ sollte so etwas wie / dev / fd / 63 “ ist ein Sonderzeichengerät, das von FD-Nummer 63 liest oder schreibt.

Antwort

Ich nehme an, Sie sprechen von bash oder einer anderen erweiterten Shell, weil das Posix Die Shell verfügt nicht über eine Prozessersetzung .

bash Handbuchseitenberichte:

Prozessersetzung
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 der Erweiterung als Argument an den aktuellen Befehl übergeben. 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, ersetzen Sie sie wird gleichzeitig mit der Parameter- und Variablenerweiterung, der Befehlssubstitution und der arithmetischen Erweiterung ausgeführt.

Mit anderen Worten und aus praktischer Sicht können Sie verwenden Ein Ausdruck wie der folgende

<(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 Prozessersetzung 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 

aber Sie kann auch die Umleitung bei der Prozessersetzung verwenden

command3 < <(command1; command2) 

, wenn command3 einen Dateiparameter akzeptiert (bei Ersetzung von stdin) )

command3 <(command1; command2) 

Kommentare

  • also < ( ) und < < () macht den gleichen Effekt, oder?
  • @solfish: nicht genau: die firse kann Wird überall dort verwendet, wo ein Dateiname erwartet wird. Die zweite ist eine Umleitung für diesen Dateinamen.

Antwort

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

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

Dies „leitet“ die Ausgabe der drei zu sortierenden Befehle weiter, da sort eine Liste von Eingabedateien in der Befehlszeile aufnehmen kann.

Kommentare

  • IIRC Die Syntax < (Befehl) ist eine reine Bash-Funktion.
  • @Philomath: ‚ ist in ZSH auch.
  • Nun, ZSH hat alles … (oder versucht es zumindest).
  • @Philomath: Wie wird die Prozessersetzung in anderen Shells implementiert?
  • @Philomath <() war wie viele erweiterte Shell-Funktionen ursprünglich eine ksh-Funktion und wurde von bash und zsh übernommen. psub ist speziell eine Fischfunktion, die nichts mit POSIX zu tun hat.

Antwort

Es ist zu beachten, dass die Prozessersetzung nicht auf die Form <(command) beschränkt ist, die die Ausgabe von command als a verwendet Datei. 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 ein Befehl, der die Prozessersetzung des Form >(command), um den gleichen Effekt zu erzielen, wäre

date > >(cat) 

Beachten Sie, dass die > bevor >(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) das Entspricht date /dev/fd/63, mit dem eine Nachricht an stderr gedruckt wird.

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

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

Grundsätzlich wird geprüft, ob beide Argumente Dateien sind (nicht unbedingt regulär) Dateien) und wenn dies der Fall ist, schreiben Sie das erste Feld jeder Zeile von "$1" mit awk in "$2". Dann lautet ein Befehl, der alles kombiniert, was bisher erwähnt wurde,

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

Dies gibt

a b c 

und entspricht

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

, aber das Folgende funktioniert nicht, und wir müssen hier die Prozessersetzung verwenden,

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

oder die entsprechende Form

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

Wenn ./psub.sh ebenfalls lautet stdin Abgesehen von dem, was oben erwähnt wurde, gibt es keine solche äquivalente Form, und in diesem Fall können wir nichts anstelle der Prozessersetzung verwenden (natürlich können Sie auch a verwenden Named Pipe oder temporäre Datei, aber das ist eine andere Geschichte).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.