Capitolo 22. Sostituzione di processo

La sostituzione di processo è analoga alla sostituzione di comando. La sostituzione di comando imposta una variabile al risultato di un comando, come elenco_dir=`ls -al` o xref=$( grep parola filedati). La sostituzione di processo, invece, invia l'output di un processo ad un altro processo (in altre parole, manda il risultato di un comando ad un altro comando).

Struttura della sostituzione di processo

comando tra parentesi

>(comando)

<(comando)

Queste istanze danno inizio alla sostituzione di processo. Per inviare i risultati del processo tra parentesi ad un altro processo, vengono usati i file /dev/fd/<n>. [1]

Nota

Non vi è nessuno spazio tra "<" o ">" e le parentesi. Se ce ne fosse uno verrebbe visualizzato un messaggio d'errore.

bash$ echo >(true)
/dev/fd/63

bash$ echo <(true)
/dev/fd/63
	      
Bash crea una pipe con due descrittori di file, --fIn e fOut--. Lo stdin di true si connette a fOut (dup2(fOut, 0)), quindi Bash passa /dev/fd/fIn come argomento ad echo. Sui sistemi che non dispongono dei file /dev/fd/<n>, Bash può usare dei file temporanei. (Grazie, S.C.)

Con la sostituzione di processo si possono confrontare gli output di due diversi comandi, o anche l'output di differenti opzioni dello stesso comando.

bash$ comm <(ls -l) <(ls -al)
total 12
-rw-rw-r--    1 bozo bozo       78 Mar 10 12:58 File0
-rw-rw-r--    1 bozo bozo       42 Mar 10 12:58 File2
-rw-rw-r--    1 bozo bozo      103 Mar 10 12:58 t2.sh
        total 20
        drwxrwxrwx    2 bozo bozo     4096 Mar 10 18:10 .
        drwx------   72 bozo bozo     4096 Mar 10 17:58 ..
        -rw-rw-r--    1 bozo bozo       78 Mar 10 12:58 File0
        -rw-rw-r--    1 bozo bozo       42 Mar 10 12:58 File2
        -rw-rw-r--    1 bozo bozo      103 Mar 10 12:58 t2.sh

Utilizzare la sostituzione di processo per confrontare il contenuto di due directory (per verificare quali file sono presenti nell'una, ma non nell'altra):

diff <(ls $prima_directory) <(ls $seconda_directory)

Alcuni altri usi ed impieghi della sostituzione di processo

cat <(ls -l) 
# Uguale a     ls -l | cat

sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin)
# Elenca tutti i file delle 3 directory principali 'bin' e li ordina.
# Notate che a 'sort' vengono inviati tre (contateli) distinti comandi.


diff <(comando1) <(comando2)  # Fornisce come output le differenze dei comandi.

tar cf >(bzip2 -c > file.tar.bz2) $nome_directory
# Richiama "tar cf /dev/fd/?? $nome_directory" e "bzip2 -c > file.tar.bz2".
#
# A causa della funzionalità di sistema di /dev/fd/<n>,
# non occorre che la pipe tra i due comandi sia una named pipe.
#
# Questa può essere emulata.
#
bzip2 -c < pipe > file.tar.bz2&
tar cf pipe $nome_directory
rm pipe
#        oppure
exec 3>&1
tar cf /dev/fd/4 $nome_directory 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&-
exec 3>&-


# Grazie Stépane Chazelas.

Un lettore ci ha inviato il seguente, interessante esempio di sostituzione di processo.

# Frammento di script preso dalla distribuzione SuSE:

while read  des what mask iface; do
# Alcuni comandi ...
done < <(route -n)


# Per verificarlo, facciamogli fare qualcosa.
while read  des what mask iface; do
  echo $des $what $mask $iface
done < <(route -n)  

# Output:
# Kernel IP routing table
# Destination Gateway Genmask Flags Metric Ref Use Iface
# 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo



# Come ha puntualizzato Stéphane Chazelas, una forma analoga, più facile da comprendere, è:
route -n | 
  while read des what mask iface; do  # Le variabili vengono impostate
                                      #+ con l'output della pipe.
    echo $des $what $mask $iface
  done  #  Produce lo stesso output del precedente.
        #  Tuttavia, come rileva Ulrich Gayer . . .
        #+ questa forma semplificata usa una subshell per il ciclo while
        #+ e, quindi, le variabili scompaiono quando la pipe termina.



#  Filip Moritz fa notare, comunque, che esiste una sottile differenza
#+ tra i due esempi precedenti, come viene mostrato di seguito.

(
route -n | while read x; do ((y++)); done
echo $y # $y risulta ancora non impostata

while read x; do ((y++)); done < <(route -n)
echo $y # $y contiene il numero delle righe dell'output di route -n
)

Più in generale
(
: | x=x
# sembra dare inizio ad una subshell come
: | ( x=x )
# mentre
x=x < <(:)
# no
)

# È utile per la verifica di csv* e operazioni analoghe.
# Ed è quello che, in effetti, fa il frammento di codice originale SuSE.
#* Comma Separated Values - Valori separati da virgole [N.d.T.]

Note

[1]

Ha lo stesso effetto di una named pipe (file temporaneo) che, infatti, una volta erano usate nella sostituzione di processo.