Imprime apenas linhas únicas que aparecem por último no arquivo de log com base na data / hora

2

Estou trabalhando com um arquivo de log com o seguinte formato:

Oct 12 01:28:26 server program: 192.168.1.105 text for 1.105 
Oct 12 01:30:00 server program: 192.168.1.104 text for 1.104 
Oct 12 01:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 01:32:39 server program: 192.168.1.101 text for 1.101 
Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105 
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103 
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101 

Eu preciso conseguir isso:

Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105 
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101

Como posso enviar a nova saída para um arquivo? Eu tentei isso:

awk '!_[$6]++ {a=$6} END{print a}' logfile

Mas isso não me dá os resultados esperados. Como eu posso usar o awk ou o sed para me dar apenas as linhas exclusivas da última vez que a correspondência da string foi vista ou baseada na data / hora?

    
por user53029 03.11.2016 / 23:42

4 respostas

3

Se você for fazer um segundo passe (o que você precisa muito bem), você também pode armazenar números de linha em vez de registros completos. Isso torna a lógica mais fácil.

awk 'NR == FNR {if (z[$6]) y[z[$6]]; z[$6] = FNR; next} !(FNR in y)' logfile logfile

Prova de exatidão:

No final do processamento de cada linha, cada número de linha processado até agora é um valor em z , ou um índice (não valor) em y , mas nunca os dois.

As linhas representadas por valores em z são, no final de cada iteração, exatamente e somente os registros mais recentes até agora vistos para cada endereço IP.

Os índices de y são, portanto, as linhas exatas que desejamos que não sejam impressas.

    
por 04.11.2016 / 03:31
3

Salve a linha inteira (usando $6 como índice da matriz) e em END itere sobre os elementos da matriz:

awk '{z[$6]=$0};END{for (i in z) print z[i]}' logfile

O resultado não será classificado ... Você pode fazer algo como:

awk '{z[$6]=NR" "$0};END{for (i in z) print z[i]}' logfile | sort -k1,1n | cut -f2-
### this space ^ is a literal TAB

que salva a linha não. mais o conteúdo da linha para poder classificar por linha no.

Outras maneiras envolvem uma segunda passagem para classificá-la por data (pois esse é um log), mas imprimirá entradas duplicadas se a entrada contiver linhas duplicadas (linhas inteiras, por exemplo) - por exemplo, com grep :

awk '{z[$6]=$0};END{for (var in z) print z[var]}' logfile | grep -Fxf- logfile

ou apenas com awk :

awk 'NR==FNR{z[$6]=$0;next}
FNR==1{for (var in z) y[z[var]]}
$0 in y' logfile logfile
    
por 04.11.2016 / 17:51
2

Se você tiver linhas apenas do mesmo dia, pode lidar com isso da seguinte forma:

sort -k6 -k3r logfile | uniq -f3 | sort -k3

Se você tiver linhas por mais de um dia, ainda pode usar essa abordagem básica, mas sua classificação terá que ser muito mais chique. O comando acima só pode manipular registros de um dia porque ele usa a parte do tempo do carimbo de data e hora (por exemplo, 02:28:26 ) como um proxy para o carimbo de data / hora inteiro.

    
por 04.11.2016 / 00:33
2

A lógica torna-se mais simples revertendo o arquivo linewise

$ tac logfile | awk '!seen[$6]++' | tac
Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105 
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103 
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101 
    
por 04.11.2016 / 03:32