Выводить только уникальные строки, которые появляются последними в файле журнала по дате/времени

Выводить только уникальные строки, которые появляются последними в файле журнала по дате/времени

Я работаю с лог-файлом следующего формата:

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 

Мне нужно добиться этого:

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

Как отправить новый вывод в файл? Я пробовал это:

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

Но это не дает мне ожидаемых результатов. Как я могу использовать awk или sed, чтобы получить только уникальные строки с последним временем, когда строка была найдена, или на основе даты/времени?

решение1

Если вы собираетесь сделать второй проход (а он вам вполне необходим), вы можете хранить только номера строк, а не полные записи. Это упрощает логику.

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

Доказательство правильности:

В конце обработки каждой строки, каждый номер строки, обработанной до сих пор,илизначение в z,илииндекс (не значение) в y, но никогда и то, и другое.

Строки, представленные значениями z, в конце каждой итерации представляют собой только последние записи, просмотренные на данный момент для каждого IP-адреса.

Индексы y, следовательно, являются точными линиями, которые мы хотимнетпечатать.

решение2

Сохраните всю строку (используя $6как индекс массива) и ENDпереберите элементы массива:

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

Однако результат не будет отсортирован... Вы можете сделать что-то вроде:

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

который сохраняет номер строки и ее содержимое, чтобы затем можно было выполнить сортировку по номеру строки.


Другие способы включают второй проход для сортировки по дате (так как это журнал), но будут печатать повторяющиеся записи, если входные данные содержат повторяющиеся строки (целые строки), например, с помощью grep:

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

или только с awk:

awk 'NR==FNR{z[$6]=$0;next}
FNR==1{for (var in z) y[z[var]]}
$0 in y' logfile logfile

решение3

Если у вас есть строки только за один день, вы можете сделать это следующим образом:

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

Если у вас есть строки для более чем одного дня, вы все равно можете использовать этот базовый подход, но ваша сортировка должна стать намного более изощренной. Приведенная выше команда может обрабатывать только записи за один день, поскольку она использует временную часть временной метки (например, 02:28:26) как прокси для всей временной метки.

решение4

Логика упрощается за счет переворота файла построчно

$ 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 

Связанный контент