
Я работаю с лог-файлом следующего формата:
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