domenica 10 febbraio 2008

Ridirezione dei comandi shell

Una cosa molto utile nelle shell Unix è la ridirezione dei comandi, ovvero la capacità di inviare l'output o reperire l'input di un comando da un file. La ridirezione viene effettuata mediante i caratteri speciali > e <. Ho notato che molto spesso gli studenti universitari tendono a confondere i tipi di riderezione, generando errori goffi nei loro programmi. In questo articolo vengono mostrate alcune semplici regole da tenere presente nell'utilizzo della ridirezione.


Ridirezione dello standard output

La ridirezione dello standard output avviene sempre con il carattere >, che deve essere seguito sempre da un file, e mai da un comando. E' possibile ridirezionare anche lo standard error, utilizzando la forma 2>. In generale, > supporta la ridirezione di ogni file descripto aperto dal processo. Ricordando che il file descriptor 1 è associato allo standard output, e il 2 allo standard error, si capisce come 2> ridirezioni appunto lo standard error. Per la stessa ragione, > e 1> svolgono la stessa funzione.

E' poi possibile ridirezionare i flussi di output specificando direttamente il file descriptor al quale fare riferimento. Ad esempio, se si ridireziona lo standard output, è possibile dire al sistema di ridirezionare lo standard error nello stesso modo. Questo si ottiene con 2>&1, che specifica che il file descriptor 2 (stderr) deve essere ridirezionato (>) come (&) il file descriptor 1. Una prassi comune è quella di avere:

comando > output.txt 2>&1

che cattura stdout e stderr e li salva assieme nel file output.txt.
Per concludere, ecco alcuni esempi:

Ecco alcuni esempi:
ls > elenco_file.txt
(memorizza la lista dei file in elenco_file.txt)
ls 1> elenco_file.txt
(come sopra)
ls > elenco_file.txt 2>errori.txt
(memorizza la lista dei file in elenco_file.txt, e gli errori, come ad esempio mancati permessi, in errori.txt)
ls > elenco_file.txt 2>&1
(memorizza la lista dei file in elenco_file.txt, assieme agli errori)
ls > elenco_file.txt 2>/dev/tty
(memorizza la lista dei file in elenco_file.txt e mostra a terminale gli errori)

ls > elenco_file.txt 2>/dev/null
(memorizza la lista dei file in elenco_file.txt e butta via gli errori)


La ridirezione in append segue le stesse regole, eccetto che utilizza la forma >> e che non prevede una forma complementare per lo stderr (non esiste 2>>&1). Si tenga comunque presente che un comando del tipo:

ls >> elenco_file.txt 2>&1

inserisce in append sia stdout che stderr.

Ridirezione dello standard input
La ridirezione dello standard input avviene sempre con il carattere <, che deve essere sempre preceduto da un comando e mai da un file. Non ha in questo caso senso specificare un numero di file descriptor, essendo lo stdin associato al file descriptor 0. Si tenga presente che, indipendentemente dal carattere di ridirezione usato, a destra di tale carattere si deve avere sempre un file, mentre a sinistra un comando. Ovvero:

comando > file
comando < file

Ridirezione fra processi
Qualora sia necessario passare lo stdout di un programma ad un altro programma, occorre utilizzare la pipe (|), che deve sempre avere alla sua sinistra e alla sua destra due comandi, mai dei file. E' quindi sbagliato scrivere qualche cosa come:

ls > wc -l
wc -l <>

mentre la forma corretta è:

ls | wc -l


Clobbing
La normale ridirezione dello stdout (>) ha un comportamento di default abbastanza pericoloso: se il file non esiste viene creato, ma se il file esiste il suo contenuto viene totalmente sovrascritto. In molti casi questo è un comportamento utile, ma rischia di far perdere il contenuto di un file per errori di battitura o di fretta. E' possibile disabiltare questo comportamento impostando il clobbing della shell. In sostanza, attivando l'opzione noclobber, la shell non consente la sovrascrittura di un file esistente (è invece consentita la ridirezione in append >>). Per settare l'opzione è sufficiente dare il comando seguente:

set -o noclobber 


e se si tenta di sovrascrivere un file esistente, la shell avverte l'utente con un errore:

echo "ciao" > elenco_file.txt

bash: elenco_file.txt: cannot overwrite existing file

E' comunque possibile evitare l'opzione di clobbing utilizzando lo speciale operatore >!, che consente di sovrascrivere il contenuto di un file sempre:

echo "ciao" >! elenco_file.txt
Si presti attenzione al fatto che il clobbing disabilita anche le funzioni di interpretazioni dei caratteri speciali della shell, ad esempio l'uso di * non funzionerà più.


Riassumendo quindi, qualora si effettui una ridirezione dello stdout, è opportuno procedere come segue:
  • se il file destinazione deve sempre essere azzerato, è bene usare l'operatore >!, così da evitare errori dovuti ad una errata impostazione del clobbing;
  • se il file destinazione potrebbe non essere sovrascritto (a seconda della volontà dell'utente), si utilizzi l'operatore di ridirezione normale (>).

Nessun commento: