2015-11-20 7 views
2

Je me suis gratté la tête au-dessus de celui-ci, espérant qu'il y a une solution simple que j'ai manquée.awk regex magic (correspond à la première occurence de personnage dans chaque ligne)

Résumé

simplifié le code suivant ne peut pas faire face à des adresses IPv6 dans le journal apache (ici abrégé) analysable à elle. Est-ce que je SED la variable avant l'analyse à AWK ou je peux changer la regex AWK pour correspondre seulement le premier ":" sur chaque ligne dans $ clog?

$ clog='djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:25 +0100] "GET /some_url HTTP/1.1" 404 37252 
bogus.com:80 200.87.62.227 - - [20/Nov/2015:01:06:27 +0100] "GET /some_url HTTP/1.1" 404 37262 
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:29 +0100] "GET /another_url HTTP/1.1" 200 11142 
ipv6.com:80 2a01:3e8:abcd:320::1 - - [20/Nov/2015:01:35:24 +0100] "GET /some_url HTTP/1.1" 200 273' 

$ echo "$clog" | awk -F '[: -]+' '{ vHost[$1]+=$13 } END { for (var in vHost) { printf "%s %.0f\n", var, vHost[var] }}' 
> bogus.com 37262 
> djerk.nl 48394 
> ipv6.com 0 

Comme on peut le voir la dernière ligne de la variable sabot $, le domaine vhost est pris, mais pas le nombre d'octets qui devrait sortir à 273 au lieu de 0.

question originale longue

Le problème que j'ai est avec le caractère ":". En plus des deux autres caractères (espace et tiret), j'ai besoin de AWK pour faire correspondre seulement la première occurrence de ":" dans chaque ligne qu'il évalue. les éléments suivants séparent chaque ligne de trois caractères, ce qui fonctionne bien, jusqu'à ce que les entrées du journal contiennent des adresses IPv6.

matrix=$(echo "$clog" | awk -F '[: -]+' '{ vHost[$1]++; Bytes[$1]+=$13 } END { for (var in vHost) { printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }}') 

Le code ci-dessus convertit les entrées de journal suivantes (contenues dans la variable saboterie de $):

djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:25 +0100] "GET /some_url HTTP/1.1" 404 37252 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)" 
bogus.com:80 200.87.62.227 - - [20/Nov/2015:01:06:27 +0100] "GET /some_url HTTP/1.1" 404 37262 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)" 
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:29 +0100] "GET /wordpress/2014/ssl-intercept-headaches HTTP/1.1" 200 11142 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4" 
djerk.nl:80 200.87.62.227 - - [20/Nov/2015:01:06:30 +0100] "GET /some_other_url HTTP/1.1" 404 37264 "-" "Safari/11601.1.56 CFNetwork/760.0.5 Darwin/15.0.0 (x86_64)" 

dans une table comme si, contenant le nom de vhost (numéro sans port TCP), hits et octet cumulatif compter. Une ligne par vhost:

djerk.nl 3 85658 
bogus.com 1 37262 

Mais les adresses IPv6 se divisent involontairement en raison de leur notation, ce qui provoque AWK pour produire une sortie fausse lorsque l'évaluation de ces entrées de journal. entrée du journal IPv6 Exemple:

djerk.nl:80 2a01:3e8:abcd:320::1 - - [20/Nov/2015:01:35:24 +0100] "POST /wordpress/wp-cron.php?doing_wp_cron=*** HTTP/1.0" 200 273 "-" "WordPress; http://www.djerk.nl/wordpress" 

Je suppose un travail autour serait à mutiler variable de sabot $ pour remplacer la première occurrence de « : » et supprimer ce caractère de la regex AWK. Mais je ne pense pas que la substitution de bash natif est capable de négocier des variables avec plusieurs lignes.

clog=$(sed 's/:/ /' <<< "$clog") 
matrix=$(echo "$clog" | awk -F '[ -]+' '{ vHost[$1]++; Bytes[$1]+=$10 } END { for (var in vHost) { printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }}') 

Cela fonctionne parce que saboterie $ est cité qui conserve la ligne RSS et exécute sed sur chaque ligne individuellement. En conséquence (et montré) la ligne AWK doit être ajustée pour ignorer ":" et saisir 10 $ au lieu de 13 $ pour le nombre d'octets.

Alors, en fin de compte, en écrivant cela, je me suis déjà donné une solution. Mais je suis sûr que quelqu'un connaîtra un moyen plus efficace.

+2

Est-il possible que vous pouvez simplifier quelque chose de facile pour nous de comprendre, mais que vous pouvez ensuite adapter la solution à votre vrai problème? Il y a juste trop de texte ci-dessus pour que je puisse le parcourir pour essayer de tout comprendre et je soupçonne que d'autres ressentiront la même chose. –

+2

Si vous imprimez $ 1, $ 13 sur chaque ligne, vous verrez que des problèmes surviennent même sans l'adresse IPv6. – hek2mgl

+2

La capture du fichier journal dans une variable avant l'extraction semble suspecte. Habituellement, vous voudriez exécuter Awk sur un flux d'octets à partir d'un fichier ou d'un socket, pas sur une variable que vous capturez. – tripleee

Répondre

4

Juste ne pas diviser la ligne entière sur deux-points. Supprimez le numéro de port du champ que vous extrayez à la place.

split($1, v, /:/); vHost[v[1]]++; ... 

Je ne vois pas pourquoi vous feriez des tirets, non plus; De toute façon, les chiffres sur le terrain sera renuméroté, donc vous retrouver avec quelque chose comme

awk '{ split($1, v, /:/); vHost[v[1]]++; Bytes[v[1]]+=$11 } 
    END { for (var in vHost) 
     printf "%s %.0f %.0f\n", var, vHost[var], Bytes[var] }' 
+0

Magnifique, merci! Je ne savais pas que c'était possible. Le code résultant pour le résumé J'ai ajouté 'echo" $ clog "| awk '{split ($ 1, v, /: /); vHost [v [1]] + = $ 11} END {pour (var dans vHost) {printf "% s% .0f \ n", var, vHôte [var]}} '' – dmgeurts