| Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
|---|---|---|
| Indietro | Avanti | |
scritta da Stéphane Chazelas e rivista dall'autore del documento
Un comando si aspetta che siano disponibili i primi tre descrittori di file. Il primo, fd 0 (lo standard input, stdin), è utilizzato per la lettura. Gli altri due (fd 1, stdout e fd 2, stderr) per la scrittura.
Ad ogni comando sono associati uno stdin, uno stdout e uno stderr. ls 2>&1 trasforma temporaneamente lo stderr del comando ls in un'unica "risorsa", lo stdout della shell.
Per convenzione, un comando legge il proprio input da fd 0 (stdin), visualizza l'output in fd 1 (stdout) e i messaggi d'errore in fd 2 (stderr). Se uno di questi descrittori di file non è aperto, si possono riscontrare dei problemi:
bash$ cat /etc/passwd >&-
cat: standard output: Bad file descriptor
|
Ad esempio, quando viene posto in esecuzione xterm, come prima cosa questo inizializza se stesso. Prima di mettere in esecuzione la shell dell'utente, xterm apre per tre volte il dispositivo di terminale (/dev/pts/<n> o qualcosa di analogo).
A questo punto Bash eredita questi tre descrittori di file, a loro volta ereditati da ogni comando (processo figlio) messo in esecuzione da Bash, tranne quando il comando viene rediretto. Redirezione vuol dire la riassegnazione uno dei descrittori di file a un altro file (o ad una pipe, o ad altro che lo consenta). I descrittori di file possono essere riassegnati localmente (per un comando, un gruppo di comandi, una subshell, if o case, cicli for o while...), oppure globalmente, per l'intera shell (usando exec).
ls > /dev/null esegue ls con il suo fd 1 connesso a /dev/null.
bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 363 bozo 0u CHR 136,1 3 /dev/pts/1 bash 363 bozo 1u CHR 136,1 3 /dev/pts/1 bash 363 bozo 2u CHR 136,1 3 /dev/pts/1 bash$ exec 2> /dev/null bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 371 bozo 0u CHR 136,1 3 /dev/pts/1 bash 371 bozo 1u CHR 136,1 3 /dev/pts/1 bash 371 bozo 2w CHR 1,3 120 /dev/null bash$ bash -c 'lsof -a -p $$ -d0,1,2' | cat COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 379 root 0u CHR 136,1 3 /dev/pts/1 lsof 379 root 1w FIFO 0,0 7118 pipe lsof 379 root 2u CHR 136,1 3 /dev/pts/1 bash$ echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2>&1)" COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 426 root 0u CHR 136,1 3 /dev/pts/1 lsof 426 root 1w FIFO 0,0 7520 pipe lsof 426 root 2w FIFO 0,0 7520 pipe |
Questo funziona per tipi differenti di redirezione.
Esercizio: Si analizzi lo script seguente.
#! /usr/bin/env bash
mkfifo /tmp/fifo1 /tmp/fifo2
while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 &
exec 7> /tmp/fifo1
exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)
exec 3>&1
(
(
(
while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr \
| tee /dev/fd/4 | tee /dev/fd/5 | tee /dev/fd/6 >&7 &
exec 3> /tmp/fifo2
echo 1st, allo stdout
sleep 1
echo 2nd, allo stderr >&2
sleep 1
echo 3rd, a fd 3 >&3
sleep 1
echo 4th, a fd 4 >&4
sleep 1
echo 5th, to fd 5 >&5
sleep 1
echo 6th, tramite una pipe | sed 's/.*/PIPE: &, to fd 5/' >&5
sleep 1
echo 7th, a fd 6 >&6
sleep 1
echo 8th, a fd 7 >&7
sleep 1
echo 9th, a fd 8 >&8
) 4>&1 >&3 3>&- | while read a; do echo "FD4: $a"; done 1>&3 5>&- 6>&-
) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&-
) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&-
rm -f /tmp/fifo1 /tmp/fifo2
# Per ogni comando e subshell, indicate il fd in uso e a cosa punta.
exit 0 |