|
In questo paragrafo evidenzieremo le
idee di fondo di Unix, che sono perfettamente in linea con lo scopo
dell'articolo in quanto sono esattamente le stesse del Perl. In
principio c'era il Multics, che doveva essere un grande sistema
operativo, ma in pratica fallì i suoi obiettivi. Lo Unics (poi
ribattezzato Unix) nacque per gioco (per davvero - serviva a far
girare un videogame) nei laboratori Bell della AT&T come una
versione ridotta e monoutente del Multics. Lo Unix era un sistema
operativo a linea di comando (come tutti quelli dei sui tempi), ma
la caratteristica che lo distingueva dagli altri era l'approccio: lo
Unix era pensato per essere un sistema comodo per lo sviluppo di
software.
C'è una filosofia dietro Unix, e
questo fatto non è chiaro ed evidente a tutti. In pratica lo
Unix è un ambiente di sviluppo, e tutti i suoi molteplici
comandi sono da considerare non tanto programmi a sè stanti,
ma componenti per la costruzione di programmi per l'utente finale. I
comandi di Unix vengono scritti in un linguaggio a basso livello, per
lo più in C. In questo modo si ha a disposizione una vasta
gamma di componenti pronti per l'assemblaggio. Per costruire i
programmi, si legano tra di loro i comandi utilizzando script di
shell. La system administration è in pratica l'arte di
programmare in linguaggio Unix. Per questo lo Unix è così
flessibile, ma anche per questo i comandi sono così complessi
e densi di opzioni ma non si trova mai quello che fa esattamente
quello che serve. I comandi servono per costruire soluzioni, non per
averle già belle e pronte.
Perchè questo approccio
funzioni, ogni comando di Unix deve essere realizzato in modo da
prendere un input, elaborarlo e produrre un output, che cambia a
secondo dei parametri specificati sulla riga di comando. L'output
prodotto da un comando poi diventa eventualmente l'input per un altro
comando. Concatenando l'output di uno all'input di un altro si
ottengono utili "pipeline" (tubature) che effettuano le
elaborazioni. Per questo motivo in ambiente Linux abbondano i
programmi a linea di comando, anche se ultimamente non scarseggiano i
programmi interattivi come Midnight Commander o Xwpe. Per
semplificare la concatenazione dei comandi, i comandi Unix producono
l'output seguendo delle convenzioni. Per esempio vediamo come due
comandi tra di loro molto diversi producono in pratica un output
simile:
[msciab@guardian /tmp]$ ls -l
total 5
drwxr-x--- 15 9543 root 1024 Dec 12 10:33 bigloo1.8
drwxr-sr-x 11 116 111 1024 Apr 5 1996 elk-3.0
drwxr-xr-x 9 215 1002 1024 Dec 12 09:55 guile-src
drwxr-xr-x 2 root root 2048 Dec 12 09:42 siod
drwxr-xr-x 11 104 111 2048 Jul 25 1995 lout.3.06
[msciab@guardian /tmp]$ mount
/dev/hda1 on / type ext2 (rw)
none on /proc type proc (rw)
/dev/hdb1 on /archive type ext2 (rw)
In generale l'output di un programma Unix può essere
considerato come una tabella, composta da record, e ogni record
contiene dei campi. L'output è un testo, diviso in righe: ogni
riga rappresenta un record; in una linea i vari campi sono separati
da spazi. Molti comandi assumono l'output in questo formato e si
comportano di conseguenza. La shell fornisce le funzioni per
incanalare l'output di un file nell'input di un altro, in modo di
comporli tra di loro. Facciamo un esempio pratico, per evidenziare le
idee di fondo di Unix
Consideriamo il problema di trovare le
10 directory più piene del nostro hard disk. Questo è
un problema tipico di un sistemista Unix, quando si sta esaurendo lo
spazio disco e vuole togliere un po' di file inutili. In ambiente
Linux, è disponibile un comando per ottenere tale risultato:
du con lo switch -S. Questo comando mostra l'uso disco
di una singola directory. L'uso di du però non è
sufficiente in quanto mostra il risultato nell'ordine di scansione
delle directory:
[root@guardian /tmp]# du -S
332 ./bigloo1.8/comptime1.8/Cfa
394 ./bigloo1.8/comptime1.8/Integrate
450 ./bigloo1.8/comptime1.8/Cgen
99 ./bigloo1.8/comptime1.8/Tvector
60 ./bigloo1.8/comptime1.8/Eval
81 ./bigloo1.8/comptime1.8/Tstruct
56 ./bigloo1.8/comptime1.8/Callcc
36 ./bigloo1.8/comptime1.8/Lifext
40 ./bigloo1.8/comptime1.8/Fail
./bigloo1.8/comptime1.8/Reduce
…
Possiamo dare in pasto l'output a sort con du -S | sort ma
non funziona, poichè otteniamo una sorpresa come questa:
[root@guardian /tmp]# du -S | sort
187 ./bigloo1.8/comptime1.8/Reduce
332 ./bigloo1.8/comptime1.8/Cfa
36 ./bigloo1.8/comptime1.8/Lifext
394 ./bigloo1.8/comptime1.8/Integrate
40 ./bigloo1.8/comptime1.8/Fail
450 ./bigloo1.8/comptime1.8/Cgen
56 ./bigloo1.8/comptime1.8/Callcc
60 ./bigloo1.8/comptime1.8/Eval
81 ./bigloo1.8/comptime1.8/Tstruct
./bigloo1.8/comptime1.8/Tvector
…
Che cosa è successo? Semplice,
l'ordinamento alfabetico fa si che l'output sia ordinato come
332 36 394! Il problema è che i numeri
dovrebbero venir stampati come 00036, 00332, 00394, … Ma in
Unix, modificare l'output di un comando prima di darlo in pasto ad un
altro è la norma. In questo caso possiamo risolvere il
problema riformattando l'output di du con awk. La
soluzione del problema è dunque:
[root@guardian /tmp]# du -S \
>| awk '{ printf "%05d %s\n", $1, $2 }' \
>| sort -r | head
01846 ./guile-src/libguile
01433 ./lout.3.06/font
01208 ./lout.3.06
01141 ./guile-src/slib
01017 ./bigloo1.8/documentation
00798 ./lout.3.06/doc/expert
00766 ./bigloo1.8/runtime1.8/.Olib/Ieee
00762 ./siod
00725 ./bigloo1.8/runtime1.8/Gc
00670 ./bigloo1.8/runtime1.8/.Olib_u/Eval
L'aspetto cruciale in questo discorso è che
il comando awk si aspetta che l'output sia costituito da
righe, e che in ogni riga ci siano dei campi separati da una sequenza
di spazi. Awk infatti processa riga per riga, e mette i valori
dei campi nelle variabili $1, $2, $3…. Poi su queste
variabili vengono eseguite semplici operazioni. Il comando du,
come pure mount o ls producono l'output proprio nel
formato che si aspetta awk. Questo è il segreto della
programmazione di shell: tutto è rappersentato come testo, e
il testo viene mantenuto nel formato il più semplice e
gestibile possibile; la programmazione Unix è elaborazione di
dati in formato testo.
|