Certains shells, comme bash
, prend en charge la substitution de processus qui est un moyen de présenter la sortie du processus sous forme de fichier, comme ceci :
$ diff <(sort file1) <(sort file2)
Cependant, cette construction n'est pas POSIX et, par conséquent, n'est pas portable. Comment la substitution de processus peut-elle être réalisée d'une manière compatible avec POSIX (c'est-à-dire celui qui fonctionne dans /bin/sh
) ?
remarque :la question ne demande pas comment différencier deux fichiers triés - ce n'est qu'un exemple artificiel pour démontrer la substitution de processus !
Réponse acceptée :
Cette fonctionnalité a été introduite par ksh
(documenté pour la première fois dans ksh86) et utilisait le /dev/fd/n
fonctionnalité
(ajoutée indépendamment dans certains systèmes BSD et AT&T plus tôt).
En ksh
et jusqu'à ksh93u, cela ne fonctionnerait que si votre système prenait en charge /dev/fd/n
. zsh, bash et ksh93u+
et au-dessus peuvent utiliser des canaux nommés temporaires (canaux nommés ajoutés dans le système III) où /dev/fd/n
ne sont pas disponibles.
Sur les systèmes où /dev/fd/n
est disponible (POSIX ne les spécifie pas),
vous pouvez effectuer une substitution de processus (par exemple, diff <(cmd1) <(cmd2)
) vous-même avec :
{
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
Cependant cela ne fonctionne pas avec ksh93
sous Linux comme là-bas, les canaux shell sont implémentés avec des paires de sockets au lieu de tuyaux et ouvrent /dev/fd/3
où fd 3 pointe vers un socket ne fonctionne pas sous Linux.
Bien que POSIX ne spécifie pas /dev/fd/n
, il spécifie des canaux nommés. Les canaux nommés fonctionnent comme des canaux normaux, sauf que vous pouvez y accéder à partir du système de fichiers. Le problème ici est que vous devez en créer des temporaires et les nettoyer ensuite, ce qui est difficile à faire de manière fiable, d'autant plus que POSIX n'a pas de mécanisme standard (comme un mktemp -d
comme on en trouve sur certains systèmes) pour créer des fichiers ou des répertoires temporaires, et la gestion du signal (pour nettoyer au raccrochage ou à l'arrêt) est également difficile à faire de manière portable.
Vous pourriez faire quelque chose comme :
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
(ne pas s'occuper du traitement du signal ici).
Connexe :Processus terminé immédiatement après l'ouverture du terminal et impossible d'ajouter des commandes ?