Bash scripting - file descriptor: differenze tra le versioni
mNessun oggetto della modifica |
m (correzione) |
||
(4 versioni intermedie di 2 utenti non mostrate) | |||
Riga 1: | Riga 1: | ||
{{ | {{Bash scripting}} | ||
__TOC__ | |||
Su Unix e Unix-like ogni processo che non è avviato in background ha di default tre '''file descriptor''', nella forma di identificativi interi: | Su Unix e Unix-like ogni processo che non è avviato in background ha di default tre '''file descriptor''', nella forma di identificativi interi: | ||
* 0 ('''stdin'''), lo ''standard input'', da cui si leggono gli input (di default quanto scritto dalla tastiera sul terminale associato); | * 0 ('''stdin'''), lo ''standard input'', da cui si leggono gli input (di default quanto scritto dalla tastiera sul terminale associato); | ||
Riga 9: | Riga 9: | ||
Si chiamano ''file descriptor'' perché sono identificativi che fanno riferimento a un file; e infatti perfino i dispositivi, anche virtuali come il terminale, sono considerati dei file. Questo permette la possibilità di associare in modo trasparente questi file descriptor standard anche a file qualsiasi, oltre che ad altri file descriptor, in modo da ridirigerne il contenuto. E disporre di due diversi file descriptor per l'output prodotto da un comando e i messaggi di errore, permette di disabilitare anche solo uno dei due, o di salvarli su due file diversi. | Si chiamano ''file descriptor'' perché sono identificativi che fanno riferimento a un file; e infatti perfino i dispositivi, anche virtuali come il terminale, sono considerati dei file. Questo permette la possibilità di associare in modo trasparente questi file descriptor standard anche a file qualsiasi, oltre che ad altri file descriptor, in modo da ridirigerne il contenuto. E disporre di due diversi file descriptor per l'output prodotto da un comando e i messaggi di errore, permette di disabilitare anche solo uno dei due, o di salvarli su due file diversi. | ||
==Redirezioni== | == Redirezioni == | ||
Alcune comuni redirezioni, da scriversi dopo un comando (la stringa ''file'' può essere anche una variabile quotata): | Alcune comuni redirezioni, da scriversi dopo un comando (la stringa ''file'' può essere anche una variabile quotata): | ||
* '''< file''' collega lo standard input al file, in modo da leggerne il contenuto. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0< file'''; | * '''< file''' collega lo standard input al file, in modo da leggerne il contenuto. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0< file'''; | ||
Riga 17: | Riga 17: | ||
* '''>&2''' scrive lo standard output sullo standard error. Il file descriptor dello standard output è implicito, mentre quello alla destra dev'essere preceduto da <code>&</code>, ma sarebbe equivalente scrivere '''1>&2'''; | * '''>&2''' scrive lo standard output sullo standard error. Il file descriptor dello standard output è implicito, mentre quello alla destra dev'essere preceduto da <code>&</code>, ma sarebbe equivalente scrivere '''1>&2'''; | ||
* '''2>&1''' scrive lo standard error sullo standard output; | * '''2>&1''' scrive lo standard error sullo standard output; | ||
* '''&> file''' (non | * '''&> file''' (non [[POSIX]], abbreviazione per: '''> file 2>&1''') invia standard output ed error sul file (con ''append'': '''&>> file'''); | ||
* '''<&-''' chiude lo standard input. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0<&-'''; | * '''<&-''' chiude lo standard input. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0<&-'''; | ||
* '''>&-''' chiude lo standard output. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''1>&-'''; | * '''>&-''' chiude lo standard output. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''1>&-'''; | ||
Riga 31: | Riga 31: | ||
# si noti la differenza con: | # si noti la differenza con: | ||
comando >&- 2>&- # standard output ed error sono chiusi, quindi se | comando >&- 2>&- # standard output ed error sono chiusi, quindi se | ||
# venissero usati, | # venissero usati, il comando fallirebbe e il suo | ||
# exit status sarebbe diverso da zero | |||
</pre> | </pre> | ||
Una pipe (<code>comando1 '''|''' comando2</code>), già trattata | Una pipe (nella forma: <code>comando1 '''|''' comando2</code>) è anch'essa una forma di redirezione, già trattata in una sezione sulle [[Bash scripting - istruzioni composte#Composizione di comandi con pipe | istruzioni composte]], in cui lo standard output di ''comando1'' è rediretto sullo standard input di ''comando2''. | ||
===Scope della redirezione=== | === Redirezioni "here document/string" === | ||
Delle redirezioni particolari dallo standard input sono possibili anche nelle forme: | |||
* '''<<parola''' (''here-document''): legge tutte le righe che seguono, finché ne incontra una corrispondente alla parola scelta, e le invia allo standard input di un comando. Nel testo inviato si applicano le espansioni delle stringhe quotate, e come se tutto il testo fosse quotato, ma le virgolette non sono considerate un carattere speciale (restano: <code>$ \ `</code>, con <code>\</code> a fare da carattere di escape). Se invece la parola è racchiusa tra apici o virgolette (per esempio: '''<<"parola"'''), si annullano tutte le espansioni fino alla chiusura di ''here-document''. Il file descriptor è implicito, ma è equivalente scrivere '''0<<parola'''; | |||
* '''<<<stringa''' (''here-string'', non ''POSIX''): invia una stringa e una riga vuota allo standard input di un comando. La stringa può essere espansa e racchiusa tra apici, virgolette o nulla come una stringa qualsiasi, in funzione delle espansioni da attivare. Il file descriptor è implicito, ma è equivalente a scrivere '''0<<<stringa''''. | |||
Per esempio: | |||
<pre> | |||
cat <<EOF | |||
Testo su più righe da inviare a cat | |||
che si limiterà a stamparlo... | |||
Sono permesse anche $VARIABILI, che saranno | |||
espanse come tutte le espansioni attivabili | |||
da $ e permesse in una stringa quotata. | |||
EOF | |||
cat <<'EOF' | |||
Testo su più righe da inviare a cat | |||
che si limiterà a stamparlo... | |||
NON sono permesse $VARIABILI, che se presenti | |||
saranno stampate letteralmente. | |||
EOF | |||
</pre> | |||
Ricorda che, a prescindere dall'indentazione del comando, il testo e la parola (negli esempi: '''EOF''') usata come linea conclusiva devono iniziare sempre a inizio riga. Esiste una sintassi alternativa con <code><<-</code> per ignorare le tabulazioni iniziali del testo, e in tal caso solo la parola dev'essere a inizio riga, ma è sconsigliabile, in quanto alcuni editor potrebbero non preservare il carattere tabulazione e trasformarlo in spazio, e visivamente non si nota differenza tra i due casi. | |||
Pipe e altre concatenazioni da aggiungere al comando utilizzante la redirezione ''here-document'' vanno scritte sulla stessa riga del comando. E, se è possibile scrivere il comando su più righe, quella successiva al comando è considerata la riga dopo la parola che chiude tutto il testo usato per la redirezione ''here-document''. | |||
=== Scope della redirezione === | |||
Si noti che lo ''scope'' (il raggio d'azione) della redirezione non serve soltanto per evitare di scrivere più volte il percorso del file, ma ne influenza anche il significato. In particolare con la redirezione dello standard input (lettura), per garantire che tutte le istruzioni continuino la lettura da dove era rimasta invece che riprenderla sempre dall'inizio, e dello standard output, per garantire che il file non venga troncato da ogni comando ma che i successivi continuino a scrivere in seguito (come se usassero la redirezione con ''append''). | Si noti che lo ''scope'' (il raggio d'azione) della redirezione non serve soltanto per evitare di scrivere più volte il percorso del file, ma ne influenza anche il significato. In particolare con la redirezione dello standard input (lettura), per garantire che tutte le istruzioni continuino la lettura da dove era rimasta invece che riprenderla sempre dall'inizio, e dello standard output, per garantire che il file non venga troncato da ogni comando ma che i successivi continuino a scrivere in seguito (come se usassero la redirezione con ''append''). | ||
Per esempio in genere è '''sbagliato''' scrivere: | Per esempio in genere è '''sbagliato''' scrivere: | ||
<pre> | <pre> | ||
while read riga < file | while read -r riga < file | ||
do | do | ||
# "$riga" contiene *SEMPRE* la prima riga del file | # "$riga" contiene *SEMPRE* la prima riga del file | ||
Riga 51: | Riga 80: | ||
Mentre quello che si vuole è probabilmente: | Mentre quello che si vuole è probabilmente: | ||
<pre> | <pre> | ||
while read riga | while read -r riga | ||
do | do | ||
# "$riga" contiene sempre una nuova linea del file | # "$riga" contiene sempre una nuova linea del file | ||
Riga 77: | Riga 106: | ||
Si noti invece che utilizzando sempre la redirezione <code>></code> per tre volte di fila, si scriverebbe il file sempre dall'inizio, con il risultato che solo l'ultima variabile sarebbe presente nel file al termine delle istruzioni. | Si noti invece che utilizzando sempre la redirezione <code>></code> per tre volte di fila, si scriverebbe il file sempre dall'inizio, con il risultato che solo l'ultima variabile sarebbe presente nel file al termine delle istruzioni. | ||
==Nuovi file descriptor== | == Nuovi file descriptor == | ||
In aggiunta ai tre file descriptor standard, è possibile aprirne di nuovi, in lettura (<code>FD<...</code>...), in scrittura (<code>FD>...</code>, append: <code>FD>>...</code>) e in lettura/scrittura (<code>FD<>...</code>), dove FD è un intero rappresentante il nuovo file descriptor e al posto dei puntini può esserci sia un file sia un file descriptor già esistente preceduto dal carattere <code>&</code>. | In aggiunta ai tre file descriptor standard, è possibile aprirne di nuovi, in lettura (<code>FD<...</code>...), in scrittura (<code>FD>...</code>, append: <code>FD>>...</code>) e in lettura/scrittura (<code>FD<>...</code>), dove FD è un intero rappresentante il nuovo file descriptor e al posto dei puntini può esserci sia un file sia un file descriptor già esistente preceduto dal carattere <code>&</code>. | ||
Riga 105: | Riga 134: | ||
Equivalentemente, con <code>exec</code>: | Equivalentemente, con <code>exec</code>: | ||
<pre> | <pre> | ||
exec 3> file | exec 3> file # apre il file descriptor in scrittura | ||
comando1 >&3 | comando1 >&3 # usa il file descriptor | ||
comando2 | comando2 | ||
comando3 >&3 | comando3 >&3 # usa il file descriptor | ||
exec 3>&- | exec 3>&- # chiude il file descriptor | ||
</pre> | </pre> | ||
Finché un file descriptor non è chiuso, non è garantita la scrittura sul file, ma la scrittura tramite file descriptor è più efficiente. | Finché un file descriptor non è chiuso, non è garantita la scrittura sul file, ma la scrittura tramite file descriptor è più efficiente. | ||
===Salvare i file descriptor=== | === Salvare i file descriptor === | ||
Si noti che <code>exec</code> è equivalente a una redirezione in un blocco, ma la chiusura dei file descriptor aperti dev'essere gestita manualmente. Prima di cambiare quelli standard, per consentirne il successivo ripristino al loro valore originale, vanno salvati in un nuovo file descriptor temporaneo: | Si noti che <code>exec</code> è equivalente a una redirezione in un blocco, ma la chiusura dei file descriptor aperti dev'essere gestita manualmente. Prima di cambiare quelli standard, per consentirne il successivo ripristino al loro valore originale, vanno salvati in un nuovo file descriptor temporaneo: | ||
<pre> | <pre> | ||
exec 3<&0 | exec 3<&0 # apre un nuovo file descriptor per salvare lo standard input | ||
exec < file # sovrascrive lo standard input | exec < file # sovrascrive lo standard input | ||
read riga | read -r riga # legge la prima riga (se esiste) | ||
read riga | read -r riga # legge la seconda riga (se esiste)! (e non di nuovo la prima) | ||
exec <&3 | exec <&3 # assegna nuovamente lo standard input al suo valore precedente | ||
exec 3<&- | exec 3<&- # chiude il nuovo file descriptor | ||
</pre> | </pre> | ||
si ricordi che invece, senza exec e senza un blocco, una redirezione (di lettura o scrittura senza append) su un singolo comando parte sempre dall'inizio: | si ricordi che invece, senza <code>exec</code> e senza un blocco, una redirezione (di lettura o scrittura senza append) su un singolo comando parte sempre dall'inizio: | ||
<pre> | <pre> | ||
read riga < file # legge la prima riga del file (se esiste) | read -r riga < file # legge la prima riga del file (se esiste) | ||
read riga < file # legge sempre la prima riga del file (se esiste)! | read -r riga < file # legge sempre la prima riga del file (se esiste)! | ||
</pre> | </pre> | ||
[[Categoria:Bash]][[Categoria:Bash_Scripting]] | [[Categoria:Bash]][[Categoria:Bash_Scripting]] |
Versione attuale delle 12:13, 18 dic 2015
Bash scripting |
Sommario |
Su Unix e Unix-like ogni processo che non è avviato in background ha di default tre file descriptor, nella forma di identificativi interi:
- 0 (stdin), lo standard input, da cui si leggono gli input (di default quanto scritto dalla tastiera sul terminale associato);
- 1 (stdout), lo standard output, a cui si inviano tutti i messaggi prodotti (di default sul terminale associato), tranne quelli relativi a messaggi d'errore;
- 2 (stderr), lo standard error, a cui si inviano tutti i messaggi di errore prodotti (di default sul terminale associato).
Lo standard input è chiuso per i processi avviati in background.
Si chiamano file descriptor perché sono identificativi che fanno riferimento a un file; e infatti perfino i dispositivi, anche virtuali come il terminale, sono considerati dei file. Questo permette la possibilità di associare in modo trasparente questi file descriptor standard anche a file qualsiasi, oltre che ad altri file descriptor, in modo da ridirigerne il contenuto. E disporre di due diversi file descriptor per l'output prodotto da un comando e i messaggi di errore, permette di disabilitare anche solo uno dei due, o di salvarli su due file diversi.
Redirezioni
Alcune comuni redirezioni, da scriversi dopo un comando (la stringa file può essere anche una variabile quotata):
- < file collega lo standard input al file, in modo da leggerne il contenuto. Il file descriptor è implicito, ma sarebbe equivalente scrivere 0< file;
- > file dopo un comando ne scrive lo standard output sul file (troncandolo, se esiste). Si noti che il file descriptor è implicito, ma sarebbe equivalente scrivere 1> file;
- >> file (append) aggiunge il contenuto dello standard output al file (creandolo, se non esiste). Il file descriptor è implicito, ma sarebbe equivalente scrivere 1>> file;
- 2> file è simile a > file, ma ridirige lo standard error anziché lo standard output sul file (con append: 2>> file);
- >&2 scrive lo standard output sullo standard error. Il file descriptor dello standard output è implicito, mentre quello alla destra dev'essere preceduto da
&
, ma sarebbe equivalente scrivere 1>&2; - 2>&1 scrive lo standard error sullo standard output;
- &> file (non POSIX, abbreviazione per: > file 2>&1) invia standard output ed error sul file (con append: &>> file);
- <&- chiude lo standard input. Il file descriptor è implicito, ma sarebbe equivalente scrivere 0<&-;
- >&- chiude lo standard output. Il file descriptor è implicito, ma sarebbe equivalente scrivere 1>&-;
- 2>&- chiude lo standard error.
Per evitare la scrittura a schermo standard output e/o standard error non vanno chiusi, oppure l'operazione genererebbe un errore (perché il file descriptor relativo non esisterebbe più), ma ridiretti su /dev/null
, un file dispositivo la cui esistenza è specificata da POSIX che non produce mai output e può essere usato per assorbire qualsiasi cosa.
Esempi:
comando &> /dev/null # non stampa niente a schermo, neanche gli errori comando > /dev/null 2>&1 # equivalente (POSIX) # si noti la differenza con: comando >&- 2>&- # standard output ed error sono chiusi, quindi se # venissero usati, il comando fallirebbe e il suo # exit status sarebbe diverso da zero
Una pipe (nella forma: comando1 | comando2
) è anch'essa una forma di redirezione, già trattata in una sezione sulle istruzioni composte, in cui lo standard output di comando1 è rediretto sullo standard input di comando2.
Redirezioni "here document/string"
Delle redirezioni particolari dallo standard input sono possibili anche nelle forme:
- <<parola (here-document): legge tutte le righe che seguono, finché ne incontra una corrispondente alla parola scelta, e le invia allo standard input di un comando. Nel testo inviato si applicano le espansioni delle stringhe quotate, e come se tutto il testo fosse quotato, ma le virgolette non sono considerate un carattere speciale (restano:
$ \ `
, con\
a fare da carattere di escape). Se invece la parola è racchiusa tra apici o virgolette (per esempio: <<"parola"), si annullano tutte le espansioni fino alla chiusura di here-document. Il file descriptor è implicito, ma è equivalente scrivere 0<<parola; - <<<stringa (here-string, non POSIX): invia una stringa e una riga vuota allo standard input di un comando. La stringa può essere espansa e racchiusa tra apici, virgolette o nulla come una stringa qualsiasi, in funzione delle espansioni da attivare. Il file descriptor è implicito, ma è equivalente a scrivere 0<<<stringa'.
Per esempio:
cat <<EOF Testo su più righe da inviare a cat che si limiterà a stamparlo... Sono permesse anche $VARIABILI, che saranno espanse come tutte le espansioni attivabili da $ e permesse in una stringa quotata. EOF cat <<'EOF' Testo su più righe da inviare a cat che si limiterà a stamparlo... NON sono permesse $VARIABILI, che se presenti saranno stampate letteralmente. EOF
Ricorda che, a prescindere dall'indentazione del comando, il testo e la parola (negli esempi: EOF) usata come linea conclusiva devono iniziare sempre a inizio riga. Esiste una sintassi alternativa con <<-
per ignorare le tabulazioni iniziali del testo, e in tal caso solo la parola dev'essere a inizio riga, ma è sconsigliabile, in quanto alcuni editor potrebbero non preservare il carattere tabulazione e trasformarlo in spazio, e visivamente non si nota differenza tra i due casi.
Pipe e altre concatenazioni da aggiungere al comando utilizzante la redirezione here-document vanno scritte sulla stessa riga del comando. E, se è possibile scrivere il comando su più righe, quella successiva al comando è considerata la riga dopo la parola che chiude tutto il testo usato per la redirezione here-document.
Scope della redirezione
Si noti che lo scope (il raggio d'azione) della redirezione non serve soltanto per evitare di scrivere più volte il percorso del file, ma ne influenza anche il significato. In particolare con la redirezione dello standard input (lettura), per garantire che tutte le istruzioni continuino la lettura da dove era rimasta invece che riprenderla sempre dall'inizio, e dello standard output, per garantire che il file non venga troncato da ogni comando ma che i successivi continuino a scrivere in seguito (come se usassero la redirezione con append).
Per esempio in genere è sbagliato scrivere:
while read -r riga < file do # "$riga" contiene *SEMPRE* la prima riga del file # quindi il ciclo può essere eseguito per sempre se ha più di una riga! done
Mentre quello che si vuole è probabilmente:
while read -r riga do # "$riga" contiene sempre una nuova linea del file # il ciclo è eseguito per ogni linea del file done < file
Si noti che leggere in questo modo un file ha senso solo per file non binari, che potrebbero contenere anche il carattere ASCII n. 0, che non può essere memorizzato in una variabile. Inoltre l'istruzione read
fallisce se non trova un "a capo", per cui l'ultima riga del file è letta all'interno del ciclo soltanto se termina con un "a capo", altrimenti è necessario controllare se è vuota la variabile dopo il ciclo.
Allo stesso modo:
{ printf %s\\n "$var" printf %s\\n "$var2" printf %s\\n "$var3" } > file
scrive il contenuto delle variabili nel file, facendole seguire da una riga vuota. È equivalente a:
printf %s\\n "$var" > file printf %s\\n "$var2" >> file # append! printf %s\\n "$var3" >> file # append!
Si noti invece che utilizzando sempre la redirezione >
per tre volte di fila, si scriverebbe il file sempre dall'inizio, con il risultato che solo l'ultima variabile sarebbe presente nel file al termine delle istruzioni.
Nuovi file descriptor
In aggiunta ai tre file descriptor standard, è possibile aprirne di nuovi, in lettura (FD<...
...), in scrittura (FD>...
, append: FD>>...
) e in lettura/scrittura (FD<>...
), dove FD è un intero rappresentante il nuovo file descriptor e al posto dei puntini può esserci sia un file sia un file descriptor già esistente preceduto dal carattere &
.
È necessario quindi determinare a quale file (o file descriptor, se preceduto da &
) il nuovo file descriptor va associato. Ci sono due modi:
- specificarlo dopo un'istruzione composta, in modo che il file descriptor sia utilizzabile normalmente all'interno;
- specificarlo con l'istruzione
exec
senza altri argomenti (o avrebbe un significato diverso).
Per usarlo invece, una volta aperto, il file descriptor va scritto subito dopo la redirezione, preceduto dal carattere &
.
Per esempio:
{ # il file descriptor 3 è definito dentro il blocco (in scrittura) comando1 >&3 # ridirige lo standard output sul nuovo file descriptor comando2 comando3 >&3 # come per comando1 } 3> file # apre in scrittura il file descriptor per il contenuto del blocco # NOTA: fuori dal blocco il nuovo file descriptor non esiste più
Sul file scelto sono ridiretti lo standard output di comando1 e comando3. È equivalente a:
comando1 > file # scrittura comando2 comando3 >> file # scrittura con append! (nel blocco è implicito)
Equivalentemente, con exec
:
exec 3> file # apre il file descriptor in scrittura comando1 >&3 # usa il file descriptor comando2 comando3 >&3 # usa il file descriptor exec 3>&- # chiude il file descriptor
Finché un file descriptor non è chiuso, non è garantita la scrittura sul file, ma la scrittura tramite file descriptor è più efficiente.
Salvare i file descriptor
Si noti che exec
è equivalente a una redirezione in un blocco, ma la chiusura dei file descriptor aperti dev'essere gestita manualmente. Prima di cambiare quelli standard, per consentirne il successivo ripristino al loro valore originale, vanno salvati in un nuovo file descriptor temporaneo:
exec 3<&0 # apre un nuovo file descriptor per salvare lo standard input exec < file # sovrascrive lo standard input read -r riga # legge la prima riga (se esiste) read -r riga # legge la seconda riga (se esiste)! (e non di nuovo la prima) exec <&3 # assegna nuovamente lo standard input al suo valore precedente exec 3<&- # chiude il nuovo file descriptor
si ricordi che invece, senza exec
e senza un blocco, una redirezione (di lettura o scrittura senza append) su un singolo comando parte sempre dall'inizio:
read -r riga < file # legge la prima riga del file (se esiste) read -r riga < file # legge sempre la prima riga del file (se esiste)!