Bash: quantidade de bytes usados em um arquivo de log agrupado por token

2

Suponha um arquivo de log grande de vários GBs e vários milhões de linhas, em que cada linha contém um token que identifica a conta do usuário que gerou a linha.

Todos os tokens têm o mesmo tamanho e podem ser encontrados na posição dentro de cada linha de registro.

O objetivo é descobrir a quantidade de bytes registrados por cada conta.

Uma maneira de fazer isso é em várias etapas, como esta:

awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'

em que o awk extrai o token (5º tokenizing de entrada por '|'), classificar | uniq extrai a lista de tokens exclusivos que aparecem no arquivo e finalmente xargs greps e conta os bytes.

Agora isso funciona, mas é terrivelmente ineficiente porque o mesmo arquivo (enorme) fica em X vezes.

Existe uma maneira mais inteligente de conseguir o mesmo via comandos shell? (onde, de maneira mais inteligente, quero dizer mais rápido e sem consumir toneladas de RAM ou armazenamento temporário, como classificar o arquivo inteiro na RAM ou classificá-lo em um arquivo tmp).

    
por Sergio 11.07.2016 / 20:15

1 resposta

2

Tente:

awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log

Exemplo

Vamos considerar este arquivo de teste:

$ cat trace.log
1|2|3|4|jerry|6
a|b|c|d|phil|f
1|2|3|4|jerry|6

O comando original produz esta saída:

$ awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'
jerry32
phil15

O comando sugerido, que percorre o arquivo apenas uma vez, produz esta saída:

$ awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log
jerry 32
phil 15

Como funciona

  • -F "|"

    Isso define o separador de campo para entrada.

  • a[$5]+=1+length($0)

    Para cada linha, adicionamos o comprimento da linha à contagem armazenada na matriz associativa a no nome de usuário desta linha.

    A quantidade length($0) não inclui a nova linha que termina a linha. Consequentemente, adicionamos um a isso para contabilizar o \n .

  • END{for (name in a) print name,a[name]}

    Depois de lermos o arquivo uma vez, imprimimos as somas.

por 11.07.2016 / 20:23