Bash Script: cuenta líneas únicas en el archivo

129

Situación:

Tengo un archivo grande (millones de líneas) que contiene direcciones IP y puertos de una captura de red de varias horas, una ip / puerto por línea. Las líneas son de este formato:

ip.ad.dre.ss[:port]

Resultado deseado:

Hay una entrada para cada paquete que recibí al iniciar sesión, por lo que hay muchas direcciones duplicadas. Me gustaría poder ejecutar esto a través de un script de shell de algún tipo que pueda reducirlo a líneas del formato

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

donde countes el número de ocurrencias de esa dirección específica (y puerto). No hay que hacer ningún trabajo especial, trate los diferentes puertos como diferentes direcciones.

Hasta ahora, estoy usando este comando para eliminar todas las direcciones IP del archivo de registro:

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

A partir de eso, puedo usar una expresión regular bastante simple para eliminar todas las direcciones IP que fueron enviadas por mi dirección (que no me importa)

Entonces puedo usar lo siguiente para extraer las entradas únicas:

sort -u ips.txt > intermediate.txt

No sé cómo puedo agregar los recuentos de líneas de alguna manera con sort.

Wug
fuente

Respuestas:

303

Puede usar el uniqcomando para obtener recuentos de líneas repetidas ordenadas:

sort ips.txt | uniq -c

Para obtener los resultados más frecuentes en la parte superior (gracias a Peter Jaric):

sort ips.txt | uniq -c | sort -bgr
Michael Hoffman
fuente
10
Me gusta cómo -bgrcasualmente parece un mnemotécnico bigger, que es lo que queremos en la parte superior.
dwanderson
1
Como una pequeña función para su .bashrco .bash_aliasesarchivo: function countuniquelines () { sort "$1" | uniq -c | sort -bgr; }. Llamar por countuniquelines myfile.txt.
Johan
No estoy seguro de por qué no sort -nr.
Nakilon
5

Para contar el número total de líneas únicas (es decir, sin considerar líneas duplicadas) podemos usar uniqo Awk con wc:

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

Las matrices de Awk son asociativas, por lo que pueden ejecutarse un poco más rápido que la ordenación.

Generando archivo de texto:

$  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
fuente
Interesante. Podría hacer una diferencia apreciable para grandes conjuntos de datos
Wug
1

Esta es la forma más rápida de obtener el recuento de las líneas repetidas y hacer que se impriman de forma agradable, de las menos frecuentes a las más frecuentes:

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

Si no le importa el rendimiento y desea algo más fácil de recordar, simplemente ejecute:

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

PD:

sort -n analiza el campo como un número, eso es correcto ya que estamos ordenando usando los recuentos.

Luca Mastrostefano
fuente
El !en {!seen[$0]++}es redundante en este caso, ya que sólo hacemos la impresión en el END.
Amir