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.