Bash Script: hitung garis unik dalam file

129

Situasi:

Saya memiliki file besar (jutaan baris) yang berisi alamat IP dan port dari tangkapan jaringan beberapa jam, satu ip / port per baris. Baris dari format ini:

ip.ad.dre.ss[:port]

Hasil yang diinginkan:

Ada entri untuk setiap paket yang saya terima saat masuk, jadi ada banyak alamat duplikat. Saya ingin dapat menjalankan ini melalui skrip shell dari beberapa jenis yang akan dapat menguranginya ke baris format

ip.ad.dre.ss[:port] count

di mana countjumlah kemunculan alamat tertentu (dan port). Tidak ada pekerjaan khusus yang harus dilakukan, perlakukan port yang berbeda sebagai alamat yang berbeda.

Sejauh ini, saya menggunakan perintah ini untuk mengikis semua alamat ip dari file log:

grep -o -E [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(:[0-9]+)? ip_traffic-1.log > ips.txt

Dari sana, saya bisa menggunakan regex yang cukup sederhana untuk menghapus semua alamat ip yang dikirim oleh alamat saya (yang tidak saya pedulikan)

Saya kemudian dapat menggunakan yang berikut untuk mengekstrak entri unik:

sort -u ips.txt > intermediate.txt

Saya tidak tahu bagaimana saya bisa menggabungkan jumlah baris entah bagaimana dengan sortir.

Wug
sumber

Jawaban:

303

Anda dapat menggunakan uniqperintah untuk mendapatkan jumlah garis yang diulang yang diurutkan:

sort ips.txt | uniq -c

Untuk mendapatkan hasil yang paling sering di atas (terima kasih kepada Peter Jaric):

sort ips.txt | uniq -c | sort -bgr
Michael Hoffman
sumber
10
Saya suka bagaimana -bgrkebetulan terlihat seperti mnemonik untuk bigger, yang kami inginkan di atas.
dwanderson
1
Sebagai fungsi kecil untuk Anda .bashrcatau .bash_aliasesberkas: function countuniquelines () { sort "$1" | uniq -c | sort -bgr; }. Panggilan oleh countuniquelines myfile.txt.
Johan
Tidak yakin kenapa tidak sort -nr.
Nakilon
5

Untuk menghitung jumlah total garis unik (yaitu tidak mempertimbangkan garis duplikat) kita dapat menggunakan uniqatau Awk dengan wc:

sort ips.txt | uniq | wc -l
awk '!seen[$0]++' ips.txt | wc -l

Array awk bersifat asosiatif sehingga dapat berjalan sedikit lebih cepat daripada menyortir.

Membuat file teks:

$  for i in {1..100000}; do echo $RANDOM; done > random.txt
$ time sort random.txt | uniq | wc -l
31175

real    0m1.193s
user    0m0.701s
sys     0m0.388s

$ time awk '!seen[$0]++' random.txt | wc -l
31175

real    0m0.675s
user    0m0.108s
sys     0m0.171s
qwr
sumber
Menarik. Mungkin membuat perbedaan yang cukup besar untuk kumpulan data besar
Wug
1

Ini adalah cara tercepat untuk mendapatkan hitungan garis yang diulang dan membuatnya dicetak dengan baik oleh yang paling jarang hingga paling sering:

awk '{!seen[$0]++}END{for (i in seen) print seen[i], i}' ips.txt | sort -n

Jika Anda tidak peduli dengan kinerja dan menginginkan sesuatu yang lebih mudah diingat, jalankan saja:

sort ips.txt | uniq -c | sort -n

PS:

sort -n parsing bidang sebagai angka, itu benar karena kita menyortir menggunakan hitungan.

Luca Mastrostefano
sumber
The !di {!seen[$0]++}berlebihan di sini, karena kami hanya melakukan pencetakan di END.
Amir