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

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

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 não me dá os resultados esperados. Como posso usar awk ou sed para me fornecer apenas as linhas exclusivas com a última vez que a correspondência da string foi vista ou com base na data/hora?

Responder1

Se você for fazer uma segunda passagem (o que é necessário), é melhor armazenar apenas 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 correção:

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

As linhas representadas pelos valores in zsão, ao final de cada iteração, exatamente e apenas os últimos registros vistos até o momento para cada endereço IP.

Os índices de ysão, portanto, as linhas exatas que desejamosnãoimprimir.

Responder2

Salve a linha inteira (usando $6como índice do array) e ENDitere sobre os elementos do array:

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

O resultado não será classificado... Você poderia 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 no. mais o conteúdo da linha para poder classificar por número de linha.


Outras maneiras envolvem uma segunda passagem para classificá-lo por data (já que este é um log), mas imprimirá entradas duplicadas se a entrada contiver linhas duplicadas (linhas inteiras, isto é) - 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

Responder3

Se você tiver linhas apenas do mesmo dia, poderá lidar com isso da seguinte maneira:

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

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

Responder4

A lógica se torna mais simples invertendo o arquivo em linha

$ 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 

informação relacionada