Bir dosyadaki metin oluşumları nasıl sayılır?


19

IP adreslerine göre sıralanmış bir günlük dosyam var, her benzersiz IP adresinin tekrarlama sayısını bulmak istiyorum. Bunu bash ile nasıl yapabilirim? Bir ipin yanındaki tekrarlama sayısını listeleme, örneğin:

5.135.134.16 count: 5
13.57.220.172: count 30
18.206.226 count:2

ve bunun gibi.

Günlüğün bir örneği:

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"

1
“Bash” ile düz kabuk mu yoksa genel olarak komut satırı mı?
tatlı

1
Kullanabileceğiniz herhangi bir veritabanı yazılımınız var mı?
SpacePhoenix


Günlük, bir veritabanı değil, bir appache2 sunucusundan. Genel kullanım durumunda bash tercih ederim. Python ve perl çözümlerini görüyorum, eğer başka biri için iyiyse, bu harika. sort -Vgerekli olmadığını düşünüyorum ama ilk sıralama ile yapıldı . Giriş sayfasının ilk 10 istismarını, ilgili alt ağları yasaklama önerileriyle birlikte sistem yöneticisine gönderdim. örneğin, Bir IP giriş sayfasına 9000 kez ulaştı. IP ve D sınıfı alt ağı artık kara listeye alındı. Eminim bunu otomatikleştirebiliriz, ancak bu farklı bir soru.
j0h

Yanıtlar:


13

Sen kullanabilirsiniz grepve uniqüzerlerinden, adres listesi için döngü ve grepsayım için tekrar:

for i in $(<log grep -o '^[^ ]*' | uniq); do
  printf '%s count %d\n' "$i" $(<log grep -c "$i")
done

grep -o '^[^ ]*'her karakterin başlangıcından ( ^) ilk satırına kadar her karakteri çıktılar , uniqtekrarlanan satırları kaldırır, böylece size bir IP adresi listesi bırakır. Komut ikamesi sayesinde fordöngü , o anda işlenen IP'yi ve ardından “sayımı” ve sayıyı yazdırarak bu listenin üzerinden geçer. İkincisi, grep -cen az bir eşleşmeye sahip satır sayısını sayan hesaplanır .

Örnek çalışma

$ 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

13
Bu çözüm, her IP adresi için bir kez giriş dosyası üzerinde tekrar tekrar yinelenir, bu da dosya büyükse çok yavaş olacaktır. Dosyayı kullanan uniq -cveya awkyalnızca bir kez okumanız gereken diğer çözümler ,
David

1
@David bu doğru, ama bu benim de grep'in önemli olduğunu bilerek ilk kez gitmek olurdu. Performans ölçülebilir bir sorun olmadıkça ... zamanından önce optimize etmeyin mi?
D. Ben Knoble

3
Daha verimli çözümün daha basit olduğu, ancak her birinin kendi başına olduğu göz önüne alındığında, bunu erken bir optimizasyon olarak adlandırmazdım.
David

Bu arada, neden yazılıyor <log grep ...ve yazılmıyor grep ... log?
Santiago

@Santiago Çünkü Stéphane Chazelas'ın burada U&L hakkında açıkladığı gibi , birçok yönden daha iyi .
tatlı

39

Sen kullanabilirsiniz cutve uniqaraçları:

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

Açıklama:

  • cut -d ' ' -f1 : ilk alanı çıkar (ip adresi)
  • uniq -c : tekrarlanan satırları rapor etme ve olay sayısını görüntüleme

6
Bir kullanabiliriz sed, örneğin sed -E 's/ *(\S*) *(\S*)/\2 count: \1/'OP istediğini tam gibi çıktı almak için.
tatlı

2
Bu, kabul edilen cevap olmalı, çünkü tatlı tarafından dosyayı tekrar tekrar okumak gerekiyor, bu yüzden çok daha yavaş. Ayrıca sort file | cut .... , dosyanın zaten sıralanıp sıralanmadığından emin değilseniz kolayca kullanabilirsiniz .
Guntram Blohm, Monica

14

Verilen çıktı biçimini özellikle gerektirmiyorsanız, önceden gönderilmiş + tabanlı yanıtı öneriyorumcutuniq

Verilen çıktı biçimine gerçekten ihtiyacınız varsa , bunu Awk ile yapmanın tek geçiş yolu

awk '{c[$1]++} END{for(i in c) print i, "count: " c[i]}' log

Bu, giriş zaten sıralandığında bir şekilde ideal değildir, çünkü tüm IP'leri gereksiz yere belleğe depolar - önceden sıralanmış durumda (daha doğrudan eşdeğer uniq -c) daha iyi, daha karmaşık bir yol olacaktır:

awk '
  NR==1 {last=$1} 
  $1 != last {print last, "count: " c[last]; last = $1} 
  {c[$1]++} 
  END {print last, "count: " c[last]}
'

Ör.

$ 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

kesilen + uniq tabanlı cevabı sed ile istenen formatta görünecek şekilde değiştirmek kolay olurdu.
Peter - Monica'yı

@ PeterA.Schneider evet olurdu - inanıyorum ki bu cevabın yorumlarında zaten belirtildi
steeldriver

Ah, evet, anlıyorum.
Peter - Monica'yı

8

İşte olası bir çözüm:

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.loggerçek dosya adıyla değiştirin .
  • komut değiştirme ifadesi $(awk '{print $1}' "$IN_FILE" | sort -u), ilk sütunun benzersiz değerlerinin bir listesini sağlar.
  • daha sonra grep -cdosyadaki bu değerlerin her birini sayar.

$ 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

1
Tercih et printf...
D. Ben Knoble

1
Bu, tüm dosyayı birden çok kez işlemeniz gerektiği anlamına gelir. IP listesini almak için bir kez ve daha sonra bulduğunuz her IP için bir kez daha.
terdon

5

Bazı 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

Bu, Steeldriver'ın garip yaklaşımı ile aynı fikirdir , ancak Perl'de. -aNedenleri otomatik dizisine her giriş hattını ayırmak için Perl @F, ilk elemanın (IP) olup, $F[0]. Böylece, anahtarları IP olan ve değerleri her IP'nin kaç kez görüldüğü $k{$F[0]}++karma oluşturur %k. }{"Tüm Girdi işlendikten sonra, en sonunda gerisini" için korkak perlspeak olduğunu. Böylece, sonunda, komut dosyası karma anahtarları üzerinde yinelenir ve geçerli anahtarı ( $_) değeri ( $k{$_}) ile birlikte yazdırır .

Ve insanlar perl'in sizi şifreli karalamalara benzeyen bir senaryo yazmaya zorlamadığını düşünüyorsa, bu daha az yoğun bir biçimde aynı şeydir:

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

4

Belki OP'nin istediği bu değildir; ancak, IP adresi uzunluğunun 15 karakterle sınırlı olacağını bilersek, büyük bir günlük dosyasındaki benzersiz IP'lere sahip sayıları uniqtek başına komutla görüntülemek için daha hızlı bir yol elde edilebilir :

$ 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] ...

Seçenekler:

-w NNsatırlardaki karakterlerden daha fazlasını karşılaştırmaz

-c satırların oluşum sayısına göre önek ekler

Alternatif olarak, tam biçimlendirilmiş çıktı için tercih ediyorum awk(IPV6 adresleri için de çalışmalıdır), 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

uniqBitişik değilse giriş dosyasında yinelenen satırları algılamayacağını unutmayın , bu nedenle sortdosya için gerekli olabilir .


1
Muhtemelen pratikte yeterince iyi, ancak köşe vakalarını belirtmeye değer. Sadece 6 muhtemelen sabit karakterler IP `sonra - - [`. Ancak teoride, adres maksimumdan 8 karaktere kadar daha kısa olabilir, bu nedenle bir tarih değişikliği böyle bir IP için sayımı bölebilir. Ve ipucu olarak, bu IPv6 için işe yaramaz.
Martin Thornton

Hoşuma gitti, uniq'in sayabileceğini bilmiyordum!
j0h

1

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))

Çıktı:

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

0
cut -f1 -d- my.log | sort | uniq -c

Açıklama: my.log öğesinin ilk alanını tire işaretleri üzerinde ayırın -ve sıralayın. uniqsıralı girdi gerekiyor. -colayları saymasını söyler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.