
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 z
sã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 y
são, portanto, as linhas exatas que desejamosnãoimprimir.
Responder2
Salve a linha inteira (usando $6
como índice do array) e END
itere 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