Bash Script: conte linhas únicas no arquivo

129

Situação:

Eu tenho um arquivo grande (milhões de linhas) contendo endereços IP e portas de uma captura de rede de várias horas, um ip / porta por linha. As linhas são deste formato:

ip.ad.dre.ss[:port]

Resultado desejado:

Há uma entrada para cada pacote que recebi durante o logon, portanto há muitos endereços duplicados. Eu gostaria de poder executar isso por meio de um script de shell de algum tipo que possa reduzi-lo a linhas do formato

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

onde counté o número de ocorrências desse endereço específico (e porta). Nenhum trabalho especial precisa ser feito; trate portas diferentes como endereços diferentes.

Até agora, estou usando este comando para raspar todos os endereços IP do arquivo de log:

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

A partir disso, posso usar uma regex bastante simples para eliminar todos os endereços IP que foram enviados pelo meu endereço (dos quais não me importo)

Posso então usar o seguinte para extrair as entradas exclusivas:

sort -u ips.txt > intermediate.txt

Não sei como agregar as contagens de linha de alguma forma com a classificação.

Wug
fonte

Respostas:

303

Você pode usar o uniqcomando para obter contagens de linhas repetidas classificadas:

sort ips.txt | uniq -c

Para obter os resultados mais frequentes no topo (graças a Peter Jaric):

sort ips.txt | uniq -c | sort -bgr
Michael Hoffman
fonte
10
Eu gosto de como -bgrcoincidentemente parece um mnemônico bigger, que é o que queremos no topo.
dwanderson
1
Como uma pequena função para o seu .bashrcou .bash_aliasesfile: function countuniquelines () { sort "$1" | uniq -c | sort -bgr; }. Ligue para countuniquelines myfile.txt.
30518 Johan Johan
Não sei por que não sort -nr.
Nakilon 23/04
5

Para contar o número total de linhas exclusivas (ou seja, sem considerar linhas duplicadas), podemos usar uniqou Awk com wc:

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

As matrizes do Awk são associativas, portanto podem ser um pouco mais rápidas que a classificação.

Gerando arquivo 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
fonte
Interessante. Pode fazer uma diferença significativa para grandes conjuntos de dados
Wug
1

Esta é a maneira mais rápida de obter a contagem das linhas repetidas e imprimi-las com muito bom gosto, sendo as menos frequentes as mais frequentes:

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

Se você não se importa com o desempenho e deseja algo mais fácil de lembrar, basta executar:

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

PS:

sort -n analisa o campo como um número, isso está correto, pois estamos classificando usando as contagens.

Luca Mastrostefano
fonte
O !in {!seen[$0]++}é redundante aqui, pois apenas fazemos a impressão no END.
Amir