venerdì 7 dicembre 2007

Redirezione e Pipelining

Una delle caratteristiche che ha portato al successo le prime versioni di Unix è sicuramente la capacità di redirigere i flussi e creare pipe. In particolare la redirezione dei tre flussi standard e l'uso delle pipe per accoppiare più comandi.
Va detto che le due cose dipendono una dall'altra. Una pipe è un meccanismo messo a disposizione dal sistema che permette di mettere in comunicazione due processi. Senza la capacità di redirigere i flussi però non si avrebbe la necessaria flessibilità per permettere di arrivare alla filosofia del "poco ma ben fatto".
Mi spiego meglio: una delle caratteristiche dei tool di Unix, a differenza di quelli di Multix, è assere realizzati per svolgere una sola semplice funzione nel miglior modo possibile.
Un esempio pratico è il comando cat, che a prima vista non fà nulla di utile; provate ad aprire una Shell e a dare il comando cat:
:~$ cat
questo ripete tutto quello che scrivo
questo ripete tutto quello che scrivo
quando batto invio lui ripete la riga pari-pari
quando batto invio lui ripete la riga pari-pari
a prima vista sembrerebbe una cosa assurda
a prima vista sembrerebbe una cosa assurda
ma chi usa Unix non potrebbe farne a meno
ma chi usa Unix non potrebbe farne a meno
per uscire da questa situazione premo ctrl+d
per uscire da questa situazione premo ctrl+d

:~$
A cosa serve un programma del genere? Se non esistesse modo di redirigere i flussi e creare pipe praticamente a nulla! Ma, con i dovuti supporti del sistema operativo, cat mi può servire a mille scopi.

I flussi e la redirezione:

Partiamo dal concetto base, forse un poco spartano a dire il vero, per il quale: per Unix tutto è un file! Con tutto intendo anche tastiera e monitor/video. Questo può sembrare una masochistica complicazione ma, in realtà, ci facilita la vita in una maniera impressionate (sia a livello di realizzazione che utilizzo di un software)! Vediamo il perchè:
  • Ho un programma che legge i dati da tastiera e li scrive a video.
  • La tastiera è un file.
  • Il video è un file.
  • Il mio programma può leggere da un file.
  • Il mio programma può scrivere su un file.
Qui serve però un'altro poco di teoria, prima di passare alla pratica, per capire bene.
  • Un processo può aprire (avere accesso a, se preferite) file durante l'esecuzione del suo programma
  • Un file aperto, da un processo, lo chiameremo flusso
  • I file si possono aprire nelle due modalità (in realtà sono di più di due, ma per ora va benone così) lettura o scrittura.
Un processo al suo avvio, cioè prima che esegua qualunque istruzione, ha già tre flussi (file) standard aperti.
  • stdin: lo standard input, normalmente la tastiera (lettura)
  • stdout: lo standard output, normalmente lo schermo (scrittura)
  • stderr: lo standard error, normalmente lo schermo (scrittura)
Per redirezione si intende sostituire a questi flussi degli altri a nostro gradimento. Ma ora si capisce meglio con la pratica:
:~/esempi$ ls
:~/esempi$ cat > pippo.txt
Ora scrivo ma cat non ripete
le mie righe a video...
Come mai?
Semplice, le stà scrivendo dentro al file pippo.txt
... provare per credere!
:~/esempi$ ls
pippo.txt
:~$
La directory esempi era vuota, lo si vede dall'output del primo ls. Dopo l'esecuzione di cat invece è comparso il file pippo.txt. Cosa è successo?
Abbiamo "imbrogliato" cat sostituendogli il flusso verso lo schermo con quello verso il file pippo.txt, che è stato appositamte creato. Tutto stà nel simbolo di redirezione dell'output >. Ora se aprissimo pippo.txt con il nostro editor di testo preferito vedremmo che contiene le righe da noi scritte. Ma, visto che vogliamo redirigere, possiamo anche usare cat per verificare. L'output di $cat < pippo.txt sarà:
Ora scrivo ma cat non ripete
le mie righe a video...
Come mai?
Semplice, le stà scrivendo dentro al file pippo.txt
... provare per credere!
Quindi, per redirigere l'input usiamo il simbolo <. La faccenda non è difficile da ricordare, basta considerare i simboli di redirezione come frecce:
  • Redirezione input - freccia da file al programma: $nomeprog < file_input
  • Redirezione output - freccia da programma al file: $nomeprog > file_output
Ovviamante le due cose possono essere fatte contemporanemte:
  • Redirezione input e output: $nomeprog > file_output < file_input
Per la redirezione dello standard output esiste una modalità speciale, chiamata append, che permette di aggiungere le righe dell'output dopo quelle già esistenti nel file. Infatti la redirezione normale dello standard output su un file già esistente provoca la sostituzione del contenuto di quest'ultimo.
  • Redirezione stdout in modalità append: $nomeprog >> file_output
Vista la redirezione, che anche certe versioni di DOS sono in grado di fare, passiamo ora ai tubi!

Pipes:

Tubi è il nome che meglio si addice a queste particolari strutture messe a disposizione da Unix. Abbiamo visto come sia facile sostituire lo stdin di un processo con un file. Abbiamo visto come sia facile farlo anche con lo stdout.
Sarebbe bello, a questo punto, vedere se si può sostituire lo stdin di un processo con lo stdout di un'altro. Secondo voi?
Dobbiamo pensare la pipe esattamante come un tubo. Un tubo attraverso il quale non scorre acqua ma caratteri. Mettere in pipe due processi significa collegarli.
Uno sguardo veloce a grep:
:~/esempi$ grep pippo
grep non è come cat
lui non ripete tutte le righe
ma solamente quelle che gli ho detto di cercare
in questo caso particolare solo quelle che
contengono la parola pippo
contengono la parola pippo

Mario và al mare
pippo và in montagna
pippo và in montagna

per uscire uso ctrl+d
:~/esempi$
Quindi dare un comando come $grep pippo vuol dire cercare nello stdin tutte le righe che contengono la parola "pippo". E se volessimo controllare quali righe contengono la parola "video" nel file pippo.txt?
:~/esempi$ cat pippo.txt | grep video
le mie righe a video...
:~/esempi$
E così abbiamo scoperto come si possono mettere in comunicazione due processi tramite pipe: $nomeprog1 | nomeprog2. Il simbolo che usiamo per creare le pipe è |. Mi rendo conto che l'esempio è stupido, infondo possiamo ottenere lo stesso risultato con: $grep video < pippo.txt. Ma è un caso dovuto alla semplicità dell'esempio. Mettiamo invece di voler controllare se sul nostro sistema gira apache:
:~/esempi$ ps ax | grep apache
2958 ? Ss 0:00 /usr/sbin/apache2 -k start
2996 ? S 0:00 /usr/sbin/apache2 -k start
2997 ? S 0:00 /usr/sbin/apache2 -k start
2998 ? S 0:00 /usr/sbin/apache2 -k start
2999 ? S 0:00 /usr/sbin/apache2 -k start
3000 ? S 0:00 /usr/sbin/apache2 -k start
3386 pts/0 R+ 0:00 grep apache
Comodo no? Senza pipe non avremo potuto dire a grep di cercare per noi nell'output di ps. Se volete avere un'idea di come la faccenda semplifichi estremamente la vita provate a dare un $ps ax e a cercare voi stessi quello che vi interessa.

In fine dei conti una pipe è un speciale file, temporaneo, che viene sostiuito allo stdout del primo programma, della linea di comando, e, contemporanemamte, allo stdin del secondo. Ci permette quindi di redirigere l'output di un processo nell'input di un'altro.

Anche le pipes possono essere multiple:
$prog1 | prog2 | prog3 | prog4

Un riassuntino grafico può aiutare a fissare le idee?

Redirezione stdout: $ prog > file

Redirezione stdin: $ prog < file

Pipe: $ prog1 | prog2

Immagini realizzate con Dia: http://live.gnome.org/Dia

Nessun commento:

Posta un commento