Einige Shells, wie bash
, unterstützen Process Substitution, was eine Möglichkeit ist, die Prozessausgabe als Datei darzustellen, wie hier:
$ diff <(sort file1) <(sort file2)
Dieses Konstrukt ist jedoch nicht POSIX und daher nicht portierbar. Wie kann eine Prozesssubstitution POSIX-freundlich erreicht werden (d. h. eine, die in /bin/sh
funktioniert ) ?
Hinweis:Die Frage ist nicht, wie man zwei sortierte Dateien unterscheidet – das ist nur ein erfundenes Beispiel, um die Prozesssubstitution zu demonstrieren!
Akzeptierte Antwort:
Diese Funktion wurde von ksh
eingeführt (zuerst dokumentiert in ksh86) und nutzte den /dev/fd/n
feature
(in einigen BSDs und AT&T-Systemen früher unabhängig hinzugefügt).
In ksh
und bis zu ksh93u würde es nicht funktionieren, es sei denn, Ihr System hätte Unterstützung für /dev/fd/n
. zsh, bash und ksh93u+
und höher können temporäre benannte Pipes (in System III hinzugefügte benannte Pipes) verwenden, wobei /dev/fd/n
sind nicht verfügbar.
Auf Systemen, auf denen /dev/fd/n
verfügbar ist (POSIX gibt diese nicht an),
können Sie eine Prozesssubstitution durchführen (z. B. diff <(cmd1) <(cmd2)
). ) selbst mit:
{
cmd1 4<&- | {
# in here fd 3 points to the reading end of the pipe
# from cmd1, while fd 0 has been restored from the original
# stdin (saved on fd 4, now closed as no longer needed)
cmd2 3<&- | diff /dev/fd/3 -
} 3<&0 <&4 4<&- # restore the original stdin for cmd2
} 4<&0 # save a copy of stdin for cmd2
Allerdings funktioniert das nicht mit ksh93
Unter Linux wie dort werden Shell-Pipes mit Socketpairs anstelle von Pipes und dem Öffnen von /dev/fd/3
implementiert wobei fd 3 auf einen Socket zeigt, funktioniert nicht unter Linux.
Obwohl POSIX /dev/fd/n
, gibt es benannte Pipes an. Benannte Pipes funktionieren wie normale Pipes, außer dass Sie vom Dateisystem darauf zugreifen können. Das Problem hier ist, dass Sie temporäre erstellen und danach bereinigen müssen, was schwer zuverlässig ist, insbesondere wenn man bedenkt, dass POSIX keinen Standardmechanismus hat (wie ein mktemp -d
wie es auf einigen Systemen zu finden ist), um temporäre Dateien oder Verzeichnisse zu erstellen, und die Signalverarbeitung (um beim Auflegen oder Beenden aufzuräumen) ist ebenfalls schwer portierbar.
Sie könnten so etwas tun:
tmpfifo() (
n=0
until
fifo=$1.$$.$n
mkfifo -m 600 -- "$fifo" 2> /dev/null
do
n=$((n + 1))
# give up after 20 attempts as it could be a permanent condition
# that prevents us from creating fifos. You'd need to raise that
# limit if you intend to create (and use at the same time)
# more than 20 fifos in your script
[ "$n" -lt 20 ] || exit 1
done
printf '%s\n' "$fifo"
)
cleanup() { rm -f -- "$fifo"; }
fifo=$(tmpfifo /tmp/fifo) || exit
cmd2 > "$fifo" & cmd1 | diff - "$fifo"
cleanup
(Hier wird nicht auf die Signalbehandlung geachtet).
Verwandte:Prozess sofort nach dem Öffnen des Terminals abgeschlossen und es ist unmöglich, Befehle hinzuzufügen?