Cercare....(grep e egrep)
Una operazione molto utile e' cercare una stringa string
in file o cercare una certa cosa nell'output di un comando.
grep
Il piu' semplice comando che si puo'usare e' grep (o egrep), la cui sintassi
e' :
grep [options] string file
dove string e' la stringa
da cercare e file
e' il file nel quale si cerca la stringa.
(N.B. in VMS la sintassi era invertita...).
Per cercare la stringa string in un file, e' sufficiente dare
grep string file
grep cerca esattamente tutti i possibili match di string
e li stampa su STDOUT.
Se si cerca string ma si cercava STRING, il comando sopra non la trova.
Si puo' allora usare l'opzione -i che cerca string
indipendentemente dalle maiuscole-minuscole.
grep -i string file
In generale e' consigliabile dare sempre l'opzione -i.
Se invece si vuole solo sapere quante volte compare string, e'
sufficiente dare:
grep -i -c string file
Ovviamente file puo' essere anche l'output di un comando o di un
programma, che puo' venire mandato in pipe al grep.
Quindi ad esempio, per cercare un particolare file di cui ci si ricorda
solo una parte di nome file_to_find_string, tra tutti i file di una
directory, e non si vuole usare find, si puo' fare:
ls -la | grep -i file_to_find_string
oppure, per cercare un particolare processo proc_to_find tra tutti i processi attivi di
una macchina, si puo' fare:
ps -edaf [-aux] | grep proc_to_find
Si possono anche cercare stringhe "complicate" usando " " per delimitare
la stringa, ad esempio,
grep -i "string1 string2" file
col quale si cerca esattamente "string1 string2".
Ma se si volesse cercare string1 o string2 ?
Si potrebbe usare ancora grep, con altre
opzioni, ma esiste un comando piu' adatto...
egrep
Il comando egrep e' simile a grep, ma piu' potente perche' fa uso fa uso delle
cosiddette Regular Expressions.
Le Regular Expressions si possono definire
come "un modo di descrivere un set di stringhe senza doverle
scrivere tutte per esteso"
(L. Wall, T. Christiansen, R. Schwartz, Programming Perl)
Ovvero sono uno strumento per descrivere insiemi di caratteri e/o stringhe
e per manipolarle. Ma qui e' necessaria una digressione.
Introduzione alle Regular Expression
Considerazioni generali
- Il termine espressione regolare e' stato coniato nei primi anni '50
dal matematico Stephen Kleene, nel corso dello sviluppo della teoria formale
dei linguaggi (sono definite come metodi per esprimere la concatenazione di elementi
di un linguaggio).
- Nell'accezione pratica le espressioni regolari sono
un potente linguaggio simbolico
che permette di rappresentare le caratteristiche di una stringa o di un insieme
di stringhe.
- L'uso delle R.E. facilita la scrittura di qualunque applicazione
nella quale sia necessario analizzare stringhe.
- Solitamente nessuno si pente di aver superato
la barriera di apprendimento iniziale delle R.E.
- Qualunque utente di Dos o Unix ha gia' usato una forma di R.E.:
> dir *.exe
- Esistono strumenti, disponibili in modo praticamente universale
(e accennati nel seguito),
che sfruttano al meglio le potenzialita' delle R.E..
Regole fondamentali
- Il carattere . (punto) nella R.E. viene soddisfatto
da qualunque carattere nella stringa originale.
"La Vispa Teresa" =~ /Vi..a T....a/
- Si puo' richiedere il valore letterale di
un qualunque carattere speciale
(+?.*^$(){}|\) precedendolo con \.
"La Vispa Teresa?" =~ /Teresa\?/
- La richiesta di un carattere o di un'espressione fra parentesi puo' essere
"moltiplicata" con l'uso di quantificatori.
- * Richiede che il carattere (o l'espressione fra parentesi) che
precede sia ripetuta zero o piu' volte. L'equivalente della
wildcard alla quale gli utenti di qualsiasi sistema operativo
sono abituati e' .*
"La Vispa Teresa" =~ /La.*Teresa/
- + Richiede che il carattere (o l'espressione fra parentesi) che
precede sia ripetuta una o piu' volte.
"avea tra l'erbetta" =~ /erbet+a/
- ? Richiede che il carattere (o l'espressione fra parentesi) che
precede sia ripetuta zero o una volta.
"La 'Vispa Teresa'" =~ /La '?Vispa/ =~ "La Vispa Teresa"
- {M,N} Richiede che il carattere (o l'espressione fra parentesi)
che precede sia ripetuta un minimo di M ed un massimo di N
volte.
- Attenzione: questi quantificatori "consumano"
quanti piu' caratteri possibili nella stringa originale (greedy
matching).
- La R.E. si puo' anche "ancorare" ad un punto preciso della stringa alla
quale viene confrontata:
- ^ Richiede che l'inizio della stringa si trovi nella posizione
indicata.
/^La Vispa/ =~ "La Vispa Teresa" !~ /^Vispa/
- $ Richiede che la fine della stringa si trovi nella
posizione indicata.
/Teresa$/ =~ "La Vispa Teresa" !~ /Vispa$/
- Anziche' richiedere un carattere preciso, si puo' richiedere che un
carattere della stringa originale sia compreso in un gruppo
o una serie di caratteri con [caratteri].
/V[ispa]{4,4} T[ersa]{5,5}/ =~
"La Vispa Teresa" =~
/La V[A-Za-z]{4,4} T[A-Za-z]{5,5}/
- Oppure si puo' chiedere che NON compaia un gruppo od una serie di
caratteri con [^caratteri].
/V[^jkwxy]+ T[^jkwxy]+/ =~
"La Vispa Teresa" =~
/[^ ]+ Teresa/
=~ "La Furba Teresa"
- Si puo' anche utilizzare il simbolo | (alternatore),
assieme alle parentesi, per realizzare
una funzione di OR logico:
"La Vispa Teresa" =~
/(Vispa|Furba) Teresa/ =~
"La Furba Teresa"
- Ci sono poi metodi piu' astuti (ma non universali) per rappresentare un gruppo
di caratteri. Ecco cosa offre Perl:
Omaggio ai gentili partecipanti
Un buon riassunto "tascabile" della sintassi delle R.E. si trova nella
guida rapida di Perl, che puo' essere ottenuta al seguente indirizzo
(e stampata con una stampante duplex):
http://www.mi.infn.it/sem-os-cntc/bigino_perl.ps
Ecco un breve estratto, con indicate le parti specifiche a Perl:
Quanto sono "standard" le R.E.?
La serie di caratteri descritte in questo documento si applica sostanzialmente
a tutte le applicazioni di uso comune: egrep, perl,
python, le librerie POSIX distribuite con i compilatori C/C++,
vi (dopo aver dato il comando set extended), eccetera.
Per le funzioni piu' avanzate la realta' si presenta piu' complicata:
Per approfondire l'argomento R.E.
Jeffrey E.F. Friedl, "Mastering Regular Expressions", O'Reilly, 1997.
Con quanto appena descritto, tornando all'uso pratico
di egrep, e' quindi possibile fare delle ricerche
piu' selettive.
Volendo cercare in un file string1 o string2,
si puo' fare:
egrep -i '(string1|string2)' file
egrep e' particolarmente utile per ricerche complesse
di particolari stringhe: linee di commento o include in un programma,
date in un log file, ecc.
Se, ad esempio, vogliamo tutte le righe di un programmma in Fortran
che non sono di commento, bastera' dare:
egrep -i '^[^c] program.f
Se invece vogliamo vedere che include locali e non di sistema ha un programma c, possiamo fare:
egrep -i '^#include[ ]+"' program.c
Ecco alcuni esempi di egrep utili (o curiosi..):
I seguenti due esempi cercano in ogni file di tipo *.pl in una certa directory,
la stringa contenente il path a Perl.
Danno esattamente lo stesso output...
egrep '[Pp]erl[0-9]' *.pl
egrep '^#\!([a-z]|/)+(P|p)erl[0-9]' *.pl
ecco un esempio piu' complesso di pipe con egrep
ls -latr *.his | egrep -i 'k+[a-z]+_dp'
questo comando ordina in senso temporale, dal piu' vecchio al piu' nuovo',file con estensione
.his nel cui nome ci sia almeno un
k seguito da almeno un'altra lettera seguita da _dp.