Alcune shell, come bash
, supporta la sostituzione del processo che è un modo per presentare l'output del processo come un file, come questo:
$ diff <(sort file1) <(sort file2)
Tuttavia, questo costrutto non è POSIX e, quindi, non portabile. Come è possibile ottenere la sostituzione del processo in modo compatibile con POSIX (cioè uno che funziona in /bin/sh
) ?
nota:la domanda non è come differenziare due file ordinati:questo è solo un esempio inventato per dimostrare la sostituzione del processo!
Risposta accettata:
Questa funzionalità è stata introdotta da ksh
(documentato per la prima volta in ksh86) e utilizzava /dev/fd/n
funzione
(aggiunta in modo indipendente in alcuni BSD e sistemi AT&T in precedenza).
In ksh
e fino a ksh93u, non funzionerebbe a meno che il tuo sistema non avesse il supporto per /dev/fd/n
. zsh, bash e ksh93u+
e versioni successive possono utilizzare named pipe temporanee (named pipe aggiunte in System III) dove /dev/fd/n
non sono disponibili.
Su sistemi in cui /dev/fd/n
è disponibile (POSIX non li specifica),
puoi eseguire la sostituzione del processo (ad esempio, diff <(cmd1) <(cmd2)
) te stesso con:
{
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
Tuttavia non funziona con ksh93
su Linux come lì, le shell pipe sono implementate con socketpairs invece di pipe e aprendo /dev/fd/3
dove fd 3 punta a un socket non funziona su Linux.
Sebbene POSIX non specifichi /dev/fd/n
, specifica pipe con nome. Le pipe con nome funzionano come le pipe normali, tranne per il fatto che è possibile accedervi dal file system. Il problema qui è che devi crearne di temporanei e ripulirli in seguito, il che è difficile da fare in modo affidabile, soprattutto considerando che POSIX non ha un meccanismo standard (come un mktemp -d
come si trova su alcuni sistemi) per creare file o directory temporanei, e anche la gestione del segnale (per ripulire dopo un riaggancio o un'interruzione) è difficile da eseguire in modo portatile.
Potresti fare qualcosa come:
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
(non occuparmi della gestione del segnale qui).
Correlati:processo completato immediatamente dopo l'apertura di Terminale e impossibile aggiungere comandi?