Newsgroup: it.comp.os.linux.sys
Titolo: Estrarre domini via regex.

Ciao a tutti!
Ho un file di log di squid di qualche centinaio di MB e vorrei crearmi
un report riportante solo la parte dell' url relativa al dominio
tralasciando directory e file del sito...
Es. Log file
qualcosa1 qualcosa2 
http://sito.dom/directory1/file1.html
 qualcosa3
qualcosa4
L' output dovrebbe essere solo:
http://sito.dom/
Sono un po' arrugginito sulle regex (le ho usate molto tempo fa in
perl) e girovagando in rete ho trovato un qualcosa tipo questo:
(ht|f)tp(s?)\:\/\/[a-zA-Z0-9\-\._]+(\.[a-zA-Z0-9\-\._]+){2,}(\/?)
che testandola su 
http://myregexp.com
 sembrerebbe proprio fare al caso
mio...
Ma come faccio ad estrarre da un file tutti e solo i match di questa
regex?

regex?
In generale,
	grep "regexp" nomefile > filerisultato.log
Leonardo
-- 
Enough, enough!  But, stranger, ere we part,
Glancing farewell to each nefarious bier,
This warning would I beg you take to heart:
There is an end to e'en the worst career!

 il conteggio per frequenza.
devo dirti che sort ha una nuova opzione che applica ance l'uniq: 
sort -u
-- 
Wikipedia Admin: "wikipedia non è un blog"
Iosif Stalin: "i compagni hanno scambiato il partito per un circolo di
discussione"

 sort -u
Penso che lo ben sappia. Quel che forse non hai notato è che uniq viene
invocato con -c e questo sort non lo fa.
-- 
Flavio Visentin
Ubuntu in Zulu significa "Non so usare Debian". (cit. CtRiX)

 
ah cieco che non sono altro! In questo modo ordina per numero di
occorrenze.


 ...
grep -E -o '(http|ftp)(|s)://[^ ]*' squid.log

 ...
grep -E -o '(http|ftp)(|s)://[^ /]*' squid.log

Penso che lo ben sappia.
Non lo sapevo quando l'ho scritto (non mi serve mai di fare sort | uniq
senza anche contare).
L'ho scoperto per coincidenza tre secondi fa rispondendo a Big Umberto,
avevo un dubbio sulla sintassi del parametro -k di sort, e, andandomela
a rivedere, mi e' cascato l'occhio sulla "-u".
E ovviamente, non mi capitera' di servirmene fin quando non me lo saro'
di nuovo dimenticato :-D -- ma grazie per il pensiero, e la fiducia :-)


> grep -E -o '(http|ftp)(|s)://[^ /]*' squid.log
Trattandosi di squid, forse sarebbe piu' adatto un:
grep -E -o '(http|ftp|CONNECT )(|://)[^ /]*' squid.log
e sempre a patto che NON venga loggato anche il referer e che NON vi siano 
stringhe url-like nel path e/o nelle query (se vengono loggate). Direi che
a scanso di problemi dovrebbe andare meglio qualcosa del tipo:
sed -rn 's/^.*((http|ftp|CONNECT )(|:\/\/)[^ /]*).*$/\1/p' squid.log
Fabio

sed -rn 's/^.*((http|ftp|CONNECT )(|:\/\/)[^ /]*).*$/\1/p' squid.log
C'e' pero' da dire che, IMHO, una lavorazione cosi' costa l'osso del
collo.
Di regola, io tolgo il levabile a colpi di cut, raffino con una grep
dove serve, e sed lo lascio per ultimo (a meno che la CPU non sia un
problema - log piccoli, lavori rari, macchina potente o poco usata).
Sono consapevole che il mio e' un approccio da macellaio :-)


 problema - log piccoli, lavori rari, macchina potente o poco usata).
Non se se eseguire 3-4-5 programmi al posto di uno possa essere più
leggero.
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito è di fare il possibile per la
salvezza degli anni nei quali viviamo,
sradicando il male dai campi che conosciamo.

 leggero.
Direi di sě. Ognuno elabora un insieme di informazioni sempre piů ridotto e
l'overhead di avere 4 programmi č solo quello di 4 fork.


 /dev/null"
real    3m20.424s
user    3m20.209s
sys     0m0.216s
===
Il reporting completo, con anche "sort | uniq -c | sort -n", occupa 45 s:
real    0m45.891s
user    1m3.872s
sys     0m1.980s
...e un "tail" mi informa che il massimo hit e'... Facebook. :-(
Ora ti lascio, devo andare a scrivere un paio di regex per cingard.
Leonardo "WILMAAAA...!"


 problema - log piccoli, lavori rari, macchina potente o poco usata).
Vero, non l'avrei fatta con sed se... il problema e' che mi ero incastrato 
sulla perlre per farla in perl e non volevo perderci altro tempo (la bimba
oramai s'era riaddormentata..., che mica son sveglio all'una, altrimenti).
Ora ho capito... era lo spazio messo dopo il CONNECT invece che a sinistra 
del | (che era pure brutto, in effetti). Rifo:
# wc -l access.log
96254 access.log
# time perl -ne 'm&^.* ((http|ftp|CONNECT)( |://)[^ /]*).*$&;print "$1
\n";' access.log > /dev/null
real    0m0.239s
user    0m0.217s
sys     0m0.015s
# time sh -c 'tr -s " " < access.log | cut -f7 -d " " | cut -f1-3 -d / > 
/dev/null'
real    0m0.884s
user    0m0.818s
sys     0m0.044s
In ogni caso quello che volevo (e che in effetti non si capisce proprio, 
alla fine) era far notare che "grep -o" e' rischioso da usare, quando ci
sono possibilita' di match multipli su una stessa linea come nel caso di
un access.log.
Ciao
Fabio

 tempo lineare,
la vera anomalia è che grep impiega sette minuti.  C'e 
qualche cosa che non va in quello che fai, non in grep


wrote:



Ce ne avrebbe impiegati 45. A quanto pare la regexp non era quella
corretta e costava di piu' del normale.
Rifacendo il test sul log - un po' ingrossato nel frattempo - ora,
effettivamente, perl impiega meno:
$ time perl -ne 'm&^.* ((http|ftp|CONNECT)( |://)[^ /]*).*$&;print
"$1\n";' zot > /dev/null
real    0m14.143s
user    0m13.825s
sys     0m0.316s
$ time sh -c 'tr -s " " < zot | cut -f7 -d " " | cut -f1-3 -d / >
/dev/null'
real    0m33.033s
user    0m59.308s
sys     0m1.600s
grep (ho tolto l'opzione CONNECT) ci mette meno di prima - ma piu'
di perl:
$ time grep -E -o "(http|ftp)(://)[^ /]*" zot > /dev/null
real    2m27.011s
user    2m26.821s
sys     0m0.192s
Ripensandoci, "(://)" non aveva piu' senso, sicche' ho riprovato:
$ time grep -E -o "(http|ftp)://[^ /]*" zot > /dev/null
real    2m17.666s
user    2m17.450s
sys     0m0.220s
...non significativo, e rimuovendo del tutto l'"or":
$ time grep -E -a -o "http://[^ /]*" zot > /dev/null
real    1m45.925s
user    1m45.763s
sys     0m0.168s
...e qui non credo di poter sbagliare qualcosa, la sintassi e' al
minimo sindacale.
Mi pare che il miglior cavallo sia il Perl... personalmente trovo
che sia la mia prima soluzione (cut) sia Perl abbiano diversi pro
e contro, per es., la mia e' piu' "facile" concettualmente, e non
devo pensare ai possibili protocolli e quant'altro, ragionando in
termini di "campi"; d'altra parte, se una riga contiene fuffa (e,
in parecchi log, puo' succedere), la fuffa la ritrovo in uscita.
Eppero' magari e' meglio sapere che c'e', piuttosto che toglierla
implicitamente usando una regexp che non la matcha; dipende dalla
situazione.
(Non volevo "sminuire" grep, sia chiaro: a me levatemi l'ossigeno
prima di levarmi grep... soffro meno :-D ).
Leonardo


 >qualche cosa che non va in quello che fai, non in grep
In effetti i miei tempi sono molto diversi: anche senza semplificare la
regexp, sed e grep mi ci mettono grossomodo lo stesso tempo.
# wc -l access.log
115361 access.log
# time sh -c "grep -E -o '(http|ftp|CONNECT )(|://)[^ /]*' access.log > 
/dev/null"
real    0m5.218s
user    0m5.021s
sys     0m0.050s
# time sed -rn 's/^.* ((http|ftp|CONNECT )(|:\/\/)[^ /]*)[ /].*/\1/p' 
access.log > /dev/null
real    0m5.921s
user    0m5.716s
sys     0m0.052s
Comunque, come accennavo precedentemente, il problema di "grep -o" non
e' di prestazioni, bensi' di qualita' dei risultati. Con "grep -o" NON 
si ottiene, nel caso piu' generico, il risultato richiesto, e nel caso 
specifico di squid ancor meno. Occorre essere certi che ci sia un solo
match per riga.
# wc -l access.log.test
2 access.log.test
# grep -E -o '(http|ftp|CONNECT )(|://)[^ /]*'  access.log.test
http://web.archive.org
http://sk.wikipedia.org
http://translate.google.it
http://sk.wikipedia.org
... lasciando per esercizio indovinare quanti e quali siti ho visitato 
e perche' "grep -o" non ha funzionato [1] (e non c'era il referer, che
avrebbe complicato ancor di piu' le cose).
Fabio
[1] ... anche se in alcuni scenari potrebbe avere senso estrarre anche
    gli url nel path e nella query.

 Occorre essere certi che ci sia un solo match per riga.
Ovviamente puoi sempre usare trucchetti:
grep -E -o '((http|ftp)://|CONNECT )[^ ]*'  access.log.test | 
grep  -E -o '^((http|ftp)://|CONNECT )[^ /]*'
PS Con AWK puoi risolvere con una sola passata. A te vedere come.

 grep  -E -o '^((http|ftp)://|CONNECT )[^ /]*'
Ovviamente non potra' funzionare. ... sorprendentemente ti continua 
a sfuggire il punto:  "grep -o" scrive una linea di output per ogni 
match, anche se avvengono sulla medesima riga di input. Vediamo con 
un esempio terra terra:
$ echo "www0 bbb www1 ccc www2"|grep -o "www."
www0
www1
www2
Una volta che hai solo l'output, come distingui gli url visitati da
quelli non visitati e riportati solo perche' erano, ad esempio, nel
path?
Fabio

 path?
Risposta: analizzando la sintassi dei log di squid riga per riga, cosa che
non si può fare con le espressioni regolari e con grep ma si può fare con 
AWH o Perl. Ma credo che stiamo dicendo la stressa cosa.

regexp, sed e grep mi ci mettono grossomodo lo stesso tempo.
Questa e' una cosa che ho osservato varie volte - ci deve essere un tot di
post in proposito su Google - senza approfondire mai.
Su alcuni sistemi, le espressioni regolari sono piu' regolari che altrove.
Per esempio, volendo dire "Una o piu' cifre", mi e' capitato... ora dovrei
ripescare i riferimenti, ma giuro che e' vero :-)... che
	[0-9]+
non funzionasse, costringendomi a usare
	[0-9][0-9]*
(poi spesso lo farei comunque, servendomi [1-9][0-9]*).
Ne ho concluso esserci vari motori regexp, murphianamente garantendo che -
quale che tu scelga - sia sempre quello sbagliato :-D.
Niente di piu' probabile che abbiano anche prestazioni diverse.
Il sistema che ho usato per i test comunque non e' modernissimo... c'e' su
una SuSE 10.3 o 10.4.
(Quasi quasi riprovo con la 11.3).


 quale che tu scelga - sia sempre quello sbagliato :-D.
  Certo: l'elemento di sintassi "+" fa parte delle espressioni regolari
estese, che grep e sed di default non utilizzano per compatibilità con
le loro versioni più vecchie.  Ad esempio:
$ echo -e 'Cento\n101\nCentodue' | grep '[0-9]+'
$ echo -e 'Cento\n101\nCentodue' | sed -n '/[0-9]+/p'
  Ma sia grep che sed riconoscono e usano la sintassi estesa con
un'opportuno switch, che è -E per grep oppure -r per sed:
$ echo -e 'Cento\n101\nCentodue' | grep -E '[0-9]+'
101
$ echo -e 'Cento\n101\nCentodue' | sed -rn '/[0-9]+/p'
101
  gawk riconosce la sintassi estesa di default, invece:
$ echo -e 'Cento\n101\nCentodue' | awk '(/[0-9]+/) {print}'
101
  Ciao,
-- 
Alessandro Selli 
http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.