PPP Copertina Rc5 |
Articoli
Realizzare dei "filtri"
I comandi e gli script solitamente utilizzati in "pipe" (cioè che accettano come standard input lo standard output di un altro comando o script) sono solitamente chiamati filtri (in analogia con le "tubazioni" che usano). Il tipico impiego di questi "filtri" è lelaborazione dei testi. Di seguito vedremo qualche utile programma per la realizzazione dei filtri (la filosofia stessa di Unix prevede la disponibilità di numerosi piccoli programmi, che possono essere combinati tra loro per svolgere tutti i compiti "quotidiani", alla stregua dei "mattoncini" delle construzioni).
I principali comandi a disposizione dell'utente per realizzare filtri "ad hoc" sono:
sort
, opera ordinando le righe in base a
particolari criteri; cut
, serve a "ritagliare" solo
alcune particolari colonne; grep
, serve a "ritagliare" solo
alcune particolari righe; tr
, serve per effettuare semplici operazioni
di conversione dei caratteri; sed
, per lavorare su testo non tabellare (è
lo stream editor); awk
, permette di lavorare agevolmente su
dati in formato tabellare (dispone di numerose funzioni
tali da renderlo un vero e proprio linguaggio di script);
perl
, uno dei migliori impieghi del perl
è proprio nella realizzazione di filtri per file di
testo (anche se ovviamente può essere impiegato per
compiti ben più nobili). Per "riga" si intende una sequenza di caratteri che terminata con il ritorno a capo; per "colonna" si intende un gruppo di caratteri separato dal successivo da un particolare delimitatore).
Di seguito vedremo sommariamente
alcuni dei comandi citati in precedenza, in particolare cut
,
grep
, tr
e sed
. Per una
descrizione di sort
riferirsi all'articolo
"I comandi buffi di Unix" (rivista Beta, numero 8
).
Per una descrizione di awk
e perl
, che
vanno ben oltre i semplici "filtri" (solo il manuale di
awk
supera le 20 pagine!), riferirsi a testi
specifici. Per awk
un buon libro è quello di Alfred
Aho, Brian Kernighan e Peter Weinberger (di tre autori di awk
appunto) "The AWK Programming Language", edito
dalla Addison-Wesley (il libro è ricco di numerosi esempi); in
alternativa procurarsi (in versione Postscript) "The GAWK
Manual" (la più versione recente dovrebbe essere la 0.15,
Aprile '93).
CUT
Questo comando è relativamente
semplice da utilizzare e permette di "ritagliare" il
testo in colonne. In particolare la forma più semplice è cut
-c lista file_in
, che preleva solo le colonne specificate.
La "lista" di colonne è una stringa che contiene i
numeri delle colonne desiderate (per specificare un intervallo di
utilizza il carattere "-
"). Ad esempio per
visualizzare solo i caratteri compresi tra la 5° colonna la 9°:
cut -c "5-9" file_input
Nel caso di testo tabellare, si
utilizzano le opzioni -f
(specificando sempre una
lista di colonne) e -d
(che specifica il
delimitatore di colonna, di default è un numero qualsiasi di
spazi e/o di tabulazioni). A esempio per visualizzare solo il
primo e il quinto campo del file /etc/passwd
, che
corrispondono al login e al nome completo, si utilizza il comando
(l'uso del quoting è in questo caso opzionale, perché né
la virgola, né i due punti hanno un significato particolare per
la shell):
cut -f "1,5" -d ":" /etc/passwd
GREP
L'utilizzo più semplice di
questo utilissimo comando è di cercare all'interno di un
file (o di più file) tutte le righe che contengono una
particolare stringa. In realtà in Unix esiste il comando fgrep
(su Linux in realtà è un link a grep -F
) che è
ottimizzato proprio per questo semplice compito.
Ad esempio per visualizzare le righe che contengono la stringa "include" in tutti i file C della directory corrente (se la stringa contiene spazi è necessario "quotarla"):
grep include *.c # oppure fgrep include *.c
Volendo è possibile invertire il
risultato con l'opzione -v
(subito dopo il
comando), nell'esempio precedente grep
visualizzerebbe tutte le righe tranne quelle che
contengono "include".
Altre opzioni utili sono: -n
(numera le righe che soddisfano il match, utile per cercarle dopo
un editor), -y
(disabilita il "case
sensitive", ossia non effettua distinzione tra maiuscole e
minuscole) , -w
(effettua la il confronto solo su
"parole") , -x
(effettua il confronto
sull'intera linea), -e
(utilizza più criteri
di confronto) e -f
(legge i "pattern" da
un file). Per una descrizione completa riferirsi alla pagina di
manuale.
Per un utilizzo più evoluto di grep
è necessario capire il concetto di "espressione
regolare", che sono dei modelli (degli schemi) del
testo da cercare. Il comando prende nome proprio da questo
concetto, infatti grep
significa proprio
"global regular expression printer" (visualizzatore di
espressioni regolari globali).
Il meccanismo è analogo a quello utilizzato dalla shell per identificare più file mediante i caratteri "jolly", in questo caso i caratteri con significato particolare vengono chiamati "metacaratteri". Purtroppo i metacaratteri delle espressioni regolari sono diversi dai corrispondenti di shell (o alcuni hanno un significato diverso all'interno della shell), per questo molte volte è necessario fare ricorso o al quoting o al carattere di escape.
I principali metacatteri sono: il punto ".
"
(corrisponde al della shell e identifica un qualsiasi carattere
non nullo), il carattere "^
" (identifica
l'inizio di una linea), il dollaro "$
"
(identifica la fine di una linea).
Ad esempio per cercare tutte le righe che cominciano con una parola di 4 lettere seguita da un punto:
grep "^....\." file_input
Oltre a questi tre metacaratteri esistono alcuni particolari costrutti come le "classi di caratteri" le "chiusure" e le "ripetizioni" delle espressioni.
Per identificare una classe (un gruppo) di
caratteri, si utilizzano le parentesi quadre e per indicare una
"gamma" di caratteri si utilizza il segno "-
";
ad esempio [0-9]
identifica tutti i numeri di una
cifra. Per negare la condizione sui caratteri si utilizza (dentro
le quadre) il carattere "^
". È importante
capire che la "gamma" si riferisce solo a singoli
caratteri, quindi [1-20]
non identifica tutti i
numeri da 1 a 20, bensì corrisponde ai caratteri 1, 2 o 0
(zero). In Linux (e su molti altri Unix) sono disponibili alcuni
costrutti particolari: [:digit:]
(identifica tutti i
numeri, corrisponde a [0-9]
), [:lower:]
(identifica i caratteri minuscoli, corrisponde a [a-z]
)
, [:upper:]
(identifica i caratteri maiuscoli,
corrisponde a [A-Z]
) , [:alnum:]
(identifica lettere e i numeri, corrisponde a [0-9A-Za-z]
).
Per identificare "chiusure e
ripetizioni", si utilizzano le parentesi graffe e una coppia
di numeri {m,n}
, dove m
corrisponde al
numero minimo di ripetizioni (di default 1) e n
il
numero massimo (di default "infinito"). Omettendo la
virgola si intende che i due valori coincidono (m=n
).
La chiusura mediante parentesi graffe, non è supportata da tutte le versioni di
grep
(in particolare su Linux per utilizzarle è necessario usare il comandogrep -E
) e soprattutto altri comandi che utilizzano le espressioni regolari non accettano questa sintassi.
L'esempio precedente può essere riscritto nel seguente modo:
grep "^.{3}\." file_input
Esistono alcuni metacaratteri che corrispondono
ad una forma abbreviata (accettata dai grep
standard):
"*
" (indica che l'espressione deve
essere ripetuta da 0 più volte e corrisponde a {0,}
),
"+
" (indica che l'espressione deve
essere ripetuta da 1 più volte e corrisponde a {1,}
),
"?
" (indica che l'espressione deve
essere ripetuta 0 o una volta e corrisponde a {0,1}
).
Ad esempio per visualizzare tutte le linee di un file tranne quelle vuote (compresi eventuali spazi):
grep -v "^ *$" file_input
Volendo utilizzare più espressioni regolari o
si utilizza l'opzione -e
prima di ciascuna
espressione o si utilizza il comando egrep
(in Linux
è meglio utilizzare grep -E
), che prevede
l'utilizzo di più espressioni, separate dal carattere
"|
" (su alcuni sistemi devono essere
racchiuse tra le parentesi tonde).
TR
Il comando tr
(transliterate)
permette di "tradurre" i caratteri di un file (o dallo
standard input), ma anche di cancellare determinati caratteri.
Anche questo comando può utilizzare le espressioni regolari (pur
con alcuni limiti). È importate notare che tr lavora solo in
pipe o con il reindirizzamento dello standard input.
L'utilizzo più semplice è convertire
tutti i caratteri contenuti nel set1
nei
corrispondenti caratteri di un nuovo set2
. Per una
corretta sostituzione la dimensione dei due set dovrebbe essere
uguale: se set2
contiene più elementi, allora si
limiterà a scartare quelli in eccedenza, ma se è set1
a
contenerne di più il risultato può essere indeterminato (su
Linux il GNU tr
espande l'ultimo carattere del set2
).
Ad esempio, per convertire tutte le maiuscole
in minuscole, nel file "testo" (notate che in Linux si
può fare a meno delle quadre e del quoting o, in alternativa, si
può usare "[:upper:]"
e "[:lower:]"
):
tr "[A-Z]" "[a-z]" < testo
Opzionalmente, sono disponibili i comandi di
chiusura (simili a quelli per le espressioni regolari), usati per
definire caratteri ripetitivi nel set2
:
l'espressione [c*9]
corrisponde a ripetere il
carattere c
per nove volte, mentre
l'espressione [c*]
adatta automaticamente il
numero di caratteri per uguagliare quello del set1
.
Un altro utilizzo di questo comodo comando è
la rimozione di alcuni caratteri o dei caratteri ripetuti,
mediante le opzioni -d
e -s
rispettivamente.
A esempio per rimuovere tutti i numeri dal file
"testo":
tr -d 0-9 < testo > nuovo_file
Volendo invece eliminare tutte le righe vuote,
costituite da due ritorni a capo (ASCII \012
) in
successione:
tr -s "\012" < testo > nuovo_file
In alternativa alla notazione ottale vista
nell'esempio precedente è possibile utilizzare alcune
sequenze di escape particolari che definiscono alcuni caratteri
non stampabili: \t
(tabulazione), \n
(newline), \r
(carriage return), \f
(form feed). Notare che in Unix ogni riga di testo è delimitata
dal carattere di newline, mentre in DOS è delimitata dalla
coppia newline e carriage return.
SED
L'editor di flusso permette di modificare
agevolmente file di testo e supporta virtualmente tutti i comandi
dell'editor di linea (ed
). Lo stesso vi
riprende in buona parte i comandi di questi due editor.
La sintassi generale è sed
"comando" file
, volendo passare più comandi
contemporaneamente o si utilizza un file di comandi richiamato
mediante il comando sed -f file_comandi file_input
,
oppure si utilizza l'espressione sed -e
"com1" -e "com2" file
.
Gli utilizzi più tipici di questo comando sono
nella sostituzione di testo (poiché dispone di operazioni più
evolute al semplice comando tr
), nella cancellazione
di testo o nella "riformattazione" del testo (ad
esempio per convertire il "ritorno a capo" DOS con il
"ritorno a capo" Unix). Di seguito vedremo solo qualche
semplice esempio; per una descrizione completa e dettagliata fate
riferimento al manuale (man sed
).
Ad esempio per sostituire nel file in
tutti
i carattere numerici con un segno "-" e mandare il
risultato in un nuovo file out
(il risultato
dell'operazione non può mai agire direttamente sul file di
input):
sed "s/[0-9]/-/g" in > out
Il comando s/cerca/sost/opz
corrisponde proprio alla sostituzione, mentre l'opzione
finale /g
serve a forzare la sostituzione
"globale" (di default sed
effettua al
massimo una sostituzione per ogni riga).
Notare come sia necessario l'utilizzo
delle virgolette (in molti casi è necessario anche
l'utilizzo del carattere di escape). Il carattere di
delimitazione dei comandi (nell'esempio "/
")
non è imposto a priori e può essere usato anche la virgola
",
", in questo caso il quoting non sarebbe
necessario (a meno di altri caratteri speciali). È possibile
l'utilizzo delle "espressioni regolari" tipiche di
grep
, in particolare esistono gli caratteri speciali
"^
" (per indicare l'inizio della
riga) e "$
" (per indicare la fine della
riga), mentre non sono disponibili le chiusure.
Uno dei limiti di è sed
di non
poter direttamente sostituire i caratteri speciali \n
e
\r
. In questo caso bisogna combinare l'utilizzo
di sed
con quello di tr
. Ad esempio la
prima riga converte un file di testo DOS in un file di testo
Unix, mentre la seconda effettua il viceversa (notare
l'utilizzo del carattere temporaneo "#
"
per gestire la sostituzione di \r
):
tr "\r" "#" < file_dos | sed "s/#$//" > file_unix sed "s/$/#/" file_unix | tr "#" "\r" > file_dos
Oltre la sostituzione dei caratteri è
possibile effettuare operazioni di cancellazione. Per cancellare
(nello standard output) tutte le righe che iniziano con il
carattere "#
" (il commento di moltissimi
file di configurazione e negli script di shell) è possibile
utilizzare il comando sed "/^#/d" file_input
.
Ad esempio, volendo contare tutte le righe di "codice"
effettivo (senza commenti e senza spazi) di uno script di shell,
è possibile utilizzare il comando:
sed -e "/^#/d" -e -e "/^[ \t]*$/d" nome_script | wc -l
Notare l'utilizzo dell'opzione -e
(come il grep
), per gestire più comandi sulla
stessa riga di sed
. Allo stesso modo è possibile
passare un "file di comandi" mediante l'opzione -f
(in questo caso un unico file può contenere numerosi comandi).
La sintassi generica di un comando sed
è la seguente (le quadre indicano un argomento opzionale):
[riga_iniziale[,riga_finale]]operazione[argomento]
Se non si specifica l'intervallo delle
righe, allora vengono considerate tutte le righe. Ad esempio per
visualizzare solo le prime 4 righe (in alternativa al comando
head
),
si può utilizzare il seguente comando (notare l'utilizzo
dell'opzione -n
per disattivare l'output e
del comando p
per stampare le righe desiderate):
sed -n 1,4p file_input
di Andrea Mauro
PPP Copertina Rc5 |