J'ai un fichier journal trié par adresses IP, je veux trouver le nombre d'occurrences de chaque adresse IP unique. Comment puis-je faire cela avec bash? Énumérant éventuellement le nombre d'occurrences à côté d'une adresse IP, comme:
5.135.134.16 count: 5
13.57.220.172: count 30
18.206.226 count:2
etc.
Voici un exemple du journal:
5.135.134.16 - - [23/Mar/2019:08:42:54 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "POST /wp-login.php HTTP/1.1" 200 3836 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "POST /wp-login.php HTTP/1.1" 200 3988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:56 -0400] "POST /xmlrpc.php HTTP/1.1" 200 413 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:05 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:06 -0400] "POST /wp-login.php HTTP/1.1" 200 3985 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:07 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:08 -0400] "POST /wp-login.php HTTP/1.1" 200 3833 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:09 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:11 -0400] "POST /wp-login.php HTTP/1.1" 200 3836 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:12 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:15 -0400] "POST /wp-login.php HTTP/1.1" 200 3837 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:17 -0400] "POST /xmlrpc.php HTTP/1.1" 200 413 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.233.99 - - [23/Mar/2019:04:17:45 -0400] "GET / HTTP/1.1" 200 25160 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "https://www.google.com/url?3a622303df89920683e4421b2cf28977" "Mozilla/5.0 (Windows NT 6.2; rv:33.0) Gecko/20100101 Firefox/33.0"
18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] "POST /wp-login.php HTTP/1.1" 200 3988 "https://www.google.com/url?3a622303df89920683e4421b2cf28977" "Mozilla/5.0 (Windows NT 6.2; rv:33.0) Gecko/20100101 Firefox/33.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
Vous pouvez utiliser grep
et uniq
pour la liste des adresses, les parcourir en boucle et grep
à nouveau pour le décompte:
for i in $(<log grep -o '^[^ ]*' | uniq); do
printf '%s count %d\n' "$i" $(<log grep -c "$i")
done
grep -o '^[^ ]*'
sort chaque caractère depuis le début (^
) jusqu'au premier espace de chaque ligne, uniq
supprime les lignes répétées, vous laissant ainsi une liste d'adresses IP. Grâce à la substitution de commande, la boucle for
fait une boucle sur cette liste en imprimant l'IP en cours de traitement suivie de "count" et du count. Ce dernier est calculé par grep -c
, qui compte le nombre de lignes avec au moins une correspondance.
$ for i in $(<log grep -o '^[^ ]*'|uniq);do printf '%s count %d\n' "$i" $(<log grep -c "$i");done
5.135.134.16 count 5
13.57.220.172 count 9
13.57.233.99 count 1
18.206.226.75 count 2
18.213.10.181 count 3
Vous pouvez utiliser les outils cut
et uniq
:
cut -d ' ' -f1 test.txt | uniq -c
5 5.135.134.16
9 13.57.220.172
1 13.57.233.99
2 18.206.226.75
3 18.213.10.181
Explication:
cut -d ' ' -f1
: extraire le premier champ (adresse IP)uniq -c
: signaler les lignes répétées et afficher le nombre d'occurrencesSi vous n'avez pas spécifiquement besoin du format de sortie donné, alors je recommanderais la réponse déjà publiée cut
+ uniq
Si vous avez vraiment besoin du format de sortie donné, une méthode en une seule passe pour le faire dans Awk serait
awk '{c[$1]++} END{for(i in c) print i, "count: " c[i]}' log
C'est quelque peu non idéal lorsque l'entrée est déjà triée car elle stocke inutilement toutes les adresses IP en mémoire - une meilleure façon, bien que plus compliquée, de le faire dans le cas pré-trié (plus directement équivalent à uniq -c
) serait:
awk '
NR==1 {last=$1}
$1 != last {print last, "count: " c[last]; last = $1}
{c[$1]++}
END {print last, "count: " c[last]}
'
Ex.
$ awk 'NR==1 {last=$1} $1 != last {print last, "count: " c[last]; last = $1} {c[$1]++} END{print last, "count: " c[last]}' log
5.135.134.16 count: 5
13.57.220.172 count: 9
13.57.233.99 count: 1
18.206.226.75 count: 2
18.213.10.181 count: 3
Voici une solution possible:
IN_FILE="file.log"
for IP in $(awk '{print $1}' "$IN_FILE" | sort -u)
do
echo -en "${IP}\tcount: "
grep -c "$IP" "$IN_FILE"
done
file.log
par le nom de fichier réel.$(awk '{print $1}' "$IN_FILE" | sort -u)
fournira une liste des valeurs uniques de la première colonne.grep -c
comptera chacune de ces valeurs dans le fichier.$ IN_FILE="file.log"; for IP in $(awk '{print $1}' "$IN_FILE" | sort -u); do echo -en "${IP}\tcount: "; grep -c "$IP" "$IN_FILE"; done
13.57.220.172 count: 9
13.57.233.99 count: 1
18.206.226.75 count: 2
18.213.10.181 count: 3
5.135.134.16 count: 5
Certains Perl:
$ Perl -lae '$k{$F[0]}++; }{ print "$_ count: $k{$_}" for keys(%k)' log
13.57.233.99 count: 1
18.206.226.75 count: 2
13.57.220.172 count: 9
5.135.134.16 count: 5
18.213.10.181 count: 3
C'est la même idée que approche awk de Steeldriver , mais en Perl. Le -a
oblige Perl à diviser automatiquement chaque ligne d'entrée dans le tableau @F
, dont le premier élément (l'IP) est $F[0]
. Donc, $k{$F[0]}++
créera le hachage %k
, dont les clés sont les IP et dont les valeurs sont le nombre de fois que chaque IP a été vue. Le }{
est une expression géniale pour "faire le reste à la toute fin, après avoir traité toutes les entrées". Ainsi, à la fin, le script itérera sur les clés du hachage et imprimera la clé actuelle ($_
) ainsi que sa valeur ($k{$_}
).
Et, juste pour que les gens ne pensent pas que Perl vous oblige à écrire un script qui ressemble à des gribouillages cryptiques, c'est la même chose sous une forme moins condensée:
Perl -e '
while (my $line=<STDIN>){
@fields = split(/ /, $line);
$ip = $fields[0];
$counts{$ip}++;
}
foreach $ip (keys(%counts)){
print "$ip count: $counts{$ip}\n"
}' < log
Ce n'est peut-être pas ce que le PO souhaite; cependant, si nous savons que la longueur de l'adresse IP sera limitée à 15 caractères, un moyen plus rapide d'afficher les décomptes avec des adresses IP uniques à partir d'un énorme fichier journal peut être obtenu en utilisant la commande uniq
seule:
$ uniq -w 15 -c log
5 5.135.134.16 - - [23/Mar/2019:08:42:54 -0400] ...
9 13.57.220.172 - - [23/Mar/2019:11:01:05 -0400] ...
1 13.57.233.99 - - [23/Mar/2019:04:17:45 -0400] ...
2 18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] ...
3 18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] ...
Options:
-w N
ne compare pas plus de N
caractères dans les lignes
-c
préfixera les lignes par le nombre d'occurrences
Alternativement, pour une sortie formatée exacte, je préfère awk
(devrait également fonctionner pour les adresses IPV6), ymmv.
$ awk 'NF { print $1 }' log | sort -h | uniq -c | awk '{printf "%s count: %d\n", $2,$1 }'
5.135.134.16 count: 5
13.57.220.172 count: 9
13.57.233.99 count: 1
18.206.226.75 count: 2
18.213.10.181 count: 3
Notez que uniq
ne détectera pas les lignes répétées dans le fichier d'entrée si elles ne sont pas adjacentes, il peut donc être nécessaire de sort
le fichier.
FWIW, Python 3:
from collections import Counter
with open('sample.log') as file:
counts = Counter(line.split()[0] for line in file)
for ip_address, count in counts.items():
print('%-15s count: %d' % (ip_address, count))
Production:
13.57.233.99 count: 1
18.213.10.181 count: 3
5.135.134.16 count: 5
18.206.226.75 count: 2
13.57.220.172 count: 9
cut -f1 -d- my.log | sort | uniq -c
Explication: prenez le premier champ de fractionnement my.log sur les tirets -
et triez-le. uniq
a besoin d'une entrée triée. -c
lui dit de compter les occurrences.