Preciso realizar uma tarefa de filtragem de atividade de bots no arquivo de log.
A solução deve mostrar apenas os registros que atendem aos seguintes critérios
- usuário logado, usuário alterado senha, usuário desconectado no mesmo segundo.
- essas ações (login, alteração de senha, logoff) aconteceram uma após a outra sem outras entradas entre elas.
Exemplo de dados de entrada
[a lot of data]
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:42 +0200|178.57.66.225|faaaaaa11111| - |user logged in| -
Mon, 22 Aug 2016 13:15:40 +0200|178.57.66.215|terdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged in| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed password| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed profile| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
Mon, 22 Aug 2016 13:20:42 +0200|178.57.67.225|faaaa0a11111| - |user logged in| -
[a lot of data]
Eu escrevi o código abaixo para concluir a tarefa
awk 'BEGIN { FS=" " } { c[$5]++; l[$5,c[$5]]=$0 } END { for (i in c) { if (c[i] == 3) for (j = 1 ; j <= c[i]; j++) print l[i,j] } }' $1
Uso:
./parse_log.sh logfile.log
Resultado:
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
Mas eu quero saber qual seria a alternativa escrita em Perl ou Python (com uso mínimo de libs externas)?
Esta não é uma resposta, mas é muito grande para um comentário e requer formatação, então para endereçar seu comentário que "O código Python é muito mais fácil de ler e entender o que ele faz.", FYI um script AWK com nomes de variáveis sensatas isso faz o que eu acho que seu script Python parece muito com seu script python, mas mais breve, pois, para manipular texto, awk já faz para você todas as coisas comuns que você precisa escrever código para fazer em python:
mas ler o arquivo inteiro na memória antes de processá-lo é uma abordagem muito ineficiente para esse problema. Você deve apenas fazer o teste e imprimir toda vez que o horário mudar:
A solução em si é escrita em Python 3.
Uso:
parse_log.py ' ' 5 logfile.log
O código Python é muito mais fácil de ler e entender o que ele faz.
Com qualquer
awk
:Com
Perl
sem usar bibliotecas externas:Com
python3
usgin apenassys
para ler o arquivo e chamarexit
com valores de significado.Com qualquer
sed
:Todas as soluções evitam armazenar todos os dados na memória
Aqui está uma solução Perl:
A linha 1 permite que o shell saiba que este é um script Perl [1].
Linha 2 "O pragma estrito desabilita certas expressões Perl que podem se comportar inesperadamente ou são difíceis de depurar, transformando-as em erros." [2]
Linha 3 "O pragma de avisos dá controle sobre quais avisos são ativados em quais partes de um programa Perl." [3]
A linha 4 declara variáveis locais [4, 5]:
$p2
é a segunda linha de entrada anterior.$p1
é a linha de entrada anterior.$x
é para salvar a parte inicial da linha de entrada atual.As linhas 5-13 formam uma
while
declaração composta [6].Na linha 5, a
while
expressão de loop usa o identificador de arquivo nulo<>
(operador de diamante) [7], permitindo que a solução seja usada como um filtro Unix [8]. Para cada linha de entrada,while
atribuirá a linha de entrada atual à variável geral de entrada padrão$_
[9] e avaliaráBLOCK
.As linhas 6-10 formam uma
if
instrução composta usando a sintaxe do modificador de instrução [10].As linhas 7-10 formam a
EXPR
parte daif
instrução, que é composta por três expressões regulares [11], umado BLOCK
função [12] e o operador lógico "and" estilo C&&
[13].Line 7 attempts to match the current line of input against the regular expression
/(.+)user logged off/
. If successful, the initial portion of the current line of input is captured into the global variable$1
[15].If line 7 was true, line 8 saves
$1
to the local variable$x
. (Subsequent regular expressions may clobber the value of$1
.) Thedo BLOCK
evaluates to the the value of the last statement ofBLOCK
, which will be a non-empty string, which Perl considers true.If line 8 was true, line 9 attempts to match the previous line of input against the regular expression
/\Q$x\Euser changed password/
.$x
must be escaped within the regular expression using the delimiters\Q
and\E
, so that its value is treated as a string.If line 9 was true, line 10 attempts to match the second previous line of input using the regular expression
/\Q$x\Euser logged in/
.If line 10 is true, line 6 prints the second previous line of input, the previous line of input, and the current line of input.
Lines 11-12 update the variables for second previous line of input and previous line of input.
The solution produces output in the same order as the input:
References:
[1] https://en.wikipedia.org/wiki/Shebang_(Unix)
[2] https://perldoc.perl.org/strict
[3] https://perldoc.perl.org/warnings
[4] https://perldoc.perl.org/perldata
[5] https://perldoc.perl.org/functions/my
[6] https://perldoc.perl.org/perlsyn#Compound-Statements
[7] https://perldoc.perl.org/perlop#I%2FO-Operators
[8] https://en.wikipedia.org/wiki/Filter_(software)#Unix
[9] https://perldoc.perl.org/perlvar#General-Variables
[10] https://perldoc.perl.org/perlsyn#Statement-Modifiers
[12] https://perldoc.perl.org/perlre
[13] https://perldoc.perl.org/functions/do
[14] https://perldoc.perl.org/perlop#C-style-Logical-And
[15] https://perldoc.perl.org/variables/$%3Cdigits%3E%20($1,%20$2,%20...)
Em Perl isso pode ser escrito como uma linha, mas parece um pouco confuso:
alternativamente, como um script:
Aqui você tem a versão python do meu script anterior (lendo do stdin)
Usando Raku (anteriormente conhecido como Perl_6)
Saída de amostra:
Se você está procurando uma linguagem de script para usar na linha de comando, considere o Raku. O código é relativamente curto e inclui algumas sutilezas - incluindo aproveitar os recursos de hash integrados do Raku.
Lendo o código acima,
lines
são read-in e cada linha estásplit
em|
pipes e/ou\s "-" \s?
whitespace-dash. Isso retorna quatro elementos por linha (zero-index = 0..3). Cada linha épush
inserida no@a
array. Em seguida, a primeira coluna de cada linha é analisada usandoDateTime::Parse.new()
para retornarposix
segundos, que são enviados para a@b
matriz.A partir dessas duas matrizes, um hash
%c
é criado usando o[Z=>]
metaoperador Zip-reduction do Raku. Isso fornece um%c
hash com segundos posix comokey
, e colunas indexadas a zero 1,2,3 comovalue
. À medida que os elementos são adicionadosappend
ao%c
hash, seus valores são anexados àposix
chave apropriada. Finalmente, no output
, para cada elemento posix-key, os valores são verificados para garantir que sejamcontain
exatamente as três strings solicitadas e também verificados se existem exatamente 9 elementos (três linhas de três colunas).Entrada de amostra:
[Obrigado a @bduggan e @sergot por atualizar o
DateTime::Parse
módulo do Raku tão rapidamente!]https://andrewshitov.com/2020/06/06/some-tips-for-working-with-hashes-in-raku/
https://github.com/sergot/datetime-parse
https://raku.org
output