Redirezione di input e output

Redirezione di input e output

Quando impartiamo dei comandi nella shell, di solito vediamo un output a video. Ad esempio:

$ echo Ciao
Ciao

In questo esempio il comando echo in pratica chiede al sistema di "stampare" la frase "ciao", ma questo output dove va?

Standard output

In un sistema Unix-like ogni programma quando viene avviato viene dotato di tre canali di comunicazioni, uno in input stdin e due in output, stdout e strderr. Internamente il programma vede questi tre canali come file aperti ai quali può accedere in maniera implicita, ad esempio con dei comandi di output come print in Python o puts() in C, oppure può indirizzarli attraverso i relativi descrittori di file in modo più o meno semplice a seconda del linguaggio, ad esempio in C c'è una variabile stderr che contiene il descrittore del file di output degli errori. Anche nella shell abbiamo a disposizione questi 3 canali, e il comando echo semplicemente invia nel canale stdout (che sta per "standard output") la frase passatagli per argomento.

Standard input

Lo standard input (o stdin) è il canale dal quale un programma riceve un input interattivo dall'utente. Ad esempio se digitiamo cat senza argomenti, questo programma rimane in attesa di un input di testo dall'utente fino a quando non viene inviato il carattere di EOF (End of File = fine del file), che sulla tastiera si ottiene premendo CTRL+d:

$ cat
La Vispa Teresa
La Vispa Teresa
avea tra l'erbetta
avea tra l'erbetta
[CTRL+d]

Vediamo un esempio un po' più utile, digitiamo il comando sort senza argomenti e poi digitiamo la lista della spesa terminandola con CTRL+d:

$ sort
banane
uva
pane
latte
[CTRL+d]
banane
latte
pane
uva

L'output del sort sarà la nostra lista della pesa in ordine alfabetico.

Le pipe

La shell offre un meccanismo semplice e potente per concatenare lo standard output di un comando con lo standard input di un altro comando attraverso il carattere | (pipe, da cui il nome). Ad esempio possiamo ottenere semplicemente l'elenco dei file presenti nella root ordinato alfabeticamente con:

$ ls / | sort
bin
boot
cdrom
dev
etc
home
[...]

In questo esempio l'output di ls / è stato utilizzato come input da sort e quindi nell'output finale che la shell ha visualizzato abbiamo la lista dei file ordinati.

La cosa bella è che si possono concatenare con le pipe quanti comando si vogliono e questo meccanismo consente di creare dei filtri sui dati semplici ma estremamente potenti. Ad esempio se volessi vedere tutti i file in root che contengono il carattere r ma ordinati alfabeticamente:

$ ls / | grep r | sort
cdrom
initrd.img
initrd.img.old
proc
root
run
srv
usr
var

Questo meccanismo di "composizione" dei comandi è alla base della filosofia Unix di avere tanti piccoli strumenti specializzati da poter utilizzare sinergicamente per ottenere un risultato complesso.

Redirezione dell'output

Per default lo stdard output è "stampato" sullo schermo del nostro terminale, ma se necessario possiamo "redirezionare" l'output di qualsiasi comando dentro ad un file usando l'operatore >. Ad esempio:

$ echo Ciao > nuovofile.txt
$ cat nuovofile.txt
Ciao

In questo caso il comando echo non ha prodotto nessun output visibile perché, grazie all'operatore > il suo output è stato utilizzato per creare il file nuovofile.txt.

Se invece di creare un nuovo file volessimo aggiungere l'output di un comando ad un file esistente, potremmo usare l'operatore >>. Ad esempio:

$ cat newfile.txt
Ciao
$ cat A presto >> newfile.txt
$ cat newfile.txt
Ciao
A presto

Standard error

Lo standard error, o stderr, è un canale di output simile allo standard output ma nel quale ogni programma può scrivere gli errori.

Proviamo ad esempio a impartire:

$ ls /directorynonesistente > test.txt

In questo caso viene visualizzato immediatamente un errore, qualcosa tipo "/directorynonesistente: file or directory not exists" e il file test.txt risulterà completamente vuoto. Questo accade perché ls invia l'errore nel canale stderr che per default viene visualizzato e noi abbiamo redirezionato con > solo il canale stdout.

Ecco l'esempio completo:

$ ls /directorynonesistente > text.txt
ls: impossibile accedere a '/directorynonesistente': File o directory non esistente
$ cat test.txt 
$ 

Piping dello standard error

In caso di necessità è possibile eseguire il piping dello standard error attraverso l'operatore |&. Ad esempio se volessimo (chissà mai per quale motivo!) stampare gli errori al contrario usando il comando rev:

$ ls /directorynonesistente |& rev
etnetsise non yrotcerid o eliF :'etnetsisenonyrotcerid/' a eredecca elibissopmi :sl

Si può anche inviare l'output in errore ad un comando, mentre l'output corretto ad un altro comando usando la sintassi: comando |& comando per l'output stderr | comando per l'output stdout

ATTENZIONE: l'ordine è importante. Prima va sempre |&

Descrittori dei files

Un descrittore di file è un numero intero che il sistema associa ad ogni canale di input/output aperto da un certo processo. Di norma nella shell stdin ha per descrittore 0, stdout ha 1 e stderr ha 2. Specificando prima dell'operatore > il canale di output redirezionare posso ad esempio redirezionare anche gli errori su un file:

$ ls /directorynonesistente >test.txt 2>err.txt
$ cat err.txt 
ls: impossibile accedere a '/directorynonesistente': File o directory non esistente

Nella shell è possibile redirezionare lo stdout o lo stderr su un diverso descrittore attraverso l'operatore >&. Ad esempio possiamo redirezionare lo standard error nello standard output con 2>&1:

$ ls /directorynonesistente >test.txt 2>&1
$ cat test.txt 
ls: impossibile accedere a '/directorynonesistente': File o directory non esistente

In pratica qui abbiamo detto alla shell di inviare lo standard output al file test.txt con >test.txt e poi anche di inviare quello che esce dallo standard error (che ha 2 come file descriptor) nello standard output (che ha 1) con 2>&1, e quindi anche lo i messaggi di errore andranno in test.txt. Notiamo che l'ordine con cui definiamo le cose è importante, nel nostro esempio ... >test.txt 2>&1 è corretto, mentre 2>&1 >test.txt non avrebbe funzionato.

csh e tcsh funzionano in modo diverso

La sintassi che abbiamo visto riguardo alla redirezione dello stderr è specifica della bourn shell e della bash, mentre le shell csh e tcsh hanno una gestione più semplice e limitata del canale standard error. In queste shell con gli operatori >& e |& si redirigono contemporaneamente i flussi stdout e stderr. Ad esempio con

$ ls >& outerr.txt

viene contemporaneamente reindirizzato sia il normale output, sia gli eventuali errori, nel file outerr.txt.

Currently there are no comments, so be the first!