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 erzeugttrue
, 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 demcd
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).