Was genau ist <() in bash (und = () in zsh)?

36

Ich fühle mich ziemlich wohl mit Bash, aber vor kurzem habe ich einen Ersatz bekommen, den ich nicht kannte.

Was genau ist <(<command>)in der Bash? Wie verhält es sich mit dem =(<command>)in zsh?

Ich verstehe, dass dies etwas mit Standard-Dateideskriptoren zu tun hat. In meinem Computer

echo <()

kehrt zurück /proc/self/fd/11, was ich als Kopie des Skripts STDOUT herausgefunden habe, aber das scheint mir immer noch ziemlich verwirrend.

Henrique Barcelos
quelle

Antworten:

51

Dies wird als Prozesssubstitution bezeichnet.

Die <(list)Syntax wird von bashund unterstützt zsh. Es bietet eine Möglichkeit, die Ausgabe eines Befehls ( list) an einen anderen Befehl zu übergeben, wenn die Verwendung einer Pipe ( |) nicht möglich ist. Zum Beispiel, wenn ein Befehl nur keine Eingabe von unterstützt STDINoder Sie die Ausgabe mehrerer Befehle benötigen:

diff <(ls dirA) <(ls dirB)

<(list)Verbindet die Ausgabe von listmit einer Datei in /dev/fd, wenn dies vom System unterstützt wird. Andernfalls wird eine Named Pipe (FIFO) verwendet (dies hängt auch von der Unterstützung durch das System ab. In keinem Handbuch wird angegeben, was passiert, wenn beide Mechanismen nicht unterstützt werden. Vermutlich wird dies abgebrochen ein Fehler). Der Name der Datei wird dann in der Befehlszeile als Argument übergeben.


zshunterstützt zusätzlich =(list)als möglicher ersatz für <(list). Bei =(list)einer temporären Datei wird anstelle von Datei /dev/fdein FIFO verwendet. Es kann als Ersatz für <(list)den Fall verwendet werden, dass das Programm in der Ausgabe suchen muss.

Laut ZSH-Handbuch kann es auch andere Probleme mit der Funktionsweise geben <(list):

Das =Formular ist nützlich, da sowohl /dev/fddie Named-Pipe-Implementierung als auch <(...)die Named- Pipe-Implementierung Nachteile aufweisen. Im ersteren Fall schließen einige Programme möglicherweise den betreffenden Dateideskriptor automatisch, bevor sie die Datei in der Befehlszeile untersuchen, insbesondere wenn dies aus Sicherheitsgründen erforderlich ist, z. B. wenn das Programm setuid ausführt. Wenn im zweiten Fall das Programm die Datei nicht öffnet, wird die Subshell, die versucht, aus der Pipe zu lesen oder in die Pipe zu schreiben, für immer blockiert (in einer typischen Implementierung haben unterschiedliche Betriebssysteme möglicherweise ein unterschiedliches Verhalten) und muss explizit beendet werden . In beiden Fällen stellt die Shell die Informationen tatsächlich über eine Pipe bereit, sodass Programme, die erwarten, in lseek(2)der Datei nach Informationen zu suchen (siehe Manpage ), nicht funktionieren.

Adaephon
quelle
Dies half mir herauszufinden, warum MacOS pfctl -f <(echo "pf rules")einen schlechten Dateideskriptor sagte. Die Verwendung von zsh und = (echo "pf rules") funktioniert stattdessen.
JohnnyB
9

Beachten Sie, dass dies eine bash Antwort ist, nicht zsh.

Es gibt Fälle in Bash, in denen Sie keine Pipes verwenden können:

some_command | some_other_command

Da Pipes Unterschalen für jede Komponente der Pipeline einführen, verschwinden beim Verlassen der Unterschalen alle Nebenwirkungen, auf die Sie sich verlassen. Zum Beispiel dieses erfundene Beispiel:

cat file | while read line; do ((count++)); done
echo $count

zeigt eine leere Zeile an, da die $countVariable in der aktuellen Shell nicht vorhanden ist.

Mit einer Bash- Prozessersetzung können Sie dieses Rätsel umgehen, indem Sie aus der Ausgabe von "some_command" wie aus einer Datei lesen

while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count   # the variable *does* exist in the current shell

(1) ist eine normale Eingabeumleitung. (2) ist der Beginn der <()Prozesssubstitutionssyntax.

Glenn Jackman
quelle
2
= (cmdlist) in zsh hat fast den gleichen Effekt wie <(cmdlist) in bash, erstellt (und löscht, wenn es fertig ist) jedoch eine temporäre Datei mit der Ausgabe von cmdlist für die Umleitung. Dies ist gut, wenn die Suche möglicherweise im Programm erfolgt. <(cmdlist) ist auch bei zsh bekannt.
Gombai Sándor