Как проанализировать многострочный файл журнала в awk и вывести только одну строку с последним известным IP-адресом

763
user882786

Я застрял и ищу помощь. Я хочу, чтобы вызвать событие, которое я хотел бы обработать через скрипт bash. Данные извлекаются из файла журнала. Прежде чем я начну объяснять, я покажу вам несколько строк этого конкретного файла журнала для лучшего понимания.

На что это похоже

test.log

[...] 24/04/2017 20:14:29 [7910] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:14:34 [10355] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:14:38 [10355] [INFO] [bob] Обработано '1' входящих изменений 24/04/2017 20:14:47 [22518] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:14:50 [7910] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:14:53 [7910] [INFO] [bob] Обработано '1' входящих изменений 24/04/2017 20:15:08 [10355] [INFO] [bob] method = 'POST' from = '192.168.0.151' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:14 [22518] [INFO] [bob] method = 'POST' from = '192.168.0.151' cmd = 'Search' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:15 [7910] [INFO] [bob] method = 'POST' from = '192.168.0.151' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:16 [10355] [INFO] [bob] method = 'POST' from = '192.168.0.151' cmd = 'Search' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:49 [32637] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:53 ​​[22518] [INFO] [bob] method = 'POST' from = '192.168.0.163' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:15:56 [22518] [INFO] [bob] Обработано '1' входящих изменений 24/04/2017 20:16:05 [10355] [INFO] [bob] method = 'POST' from = '192.168.0.151' getUser = 'bob' некоторые другие столбцы 24/04/2017 20:16:09 [32637] [INFO] [bob] method = 'POST' from = '192.168.0.151' getUser = 'bob' некоторые другие столбцы 01/05/2017 03:27:45 [4985] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 01/01/2017 03:27:49 [13971] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 01/01/2017 03:28:05 [13970] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 01/05/2017 03:28:10 [4985] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 01/05/2017 03:28:25 [13971] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 01/01/2017 03:28:31 [13970] [INFO] [alice] method = 'POST' from = '192.168.0.153' getUser = 'alice' некоторые другие столбцы 15/03/2018 14:49:19 [12918] [INFO] [alice] method = 'POST' from = '192.168.0.171' getUser = 'alice' некоторые другие столбцы 15/03/2018 14:49:21 [12834] [INFO] [alice] method = 'POST' from = '192.168.0.171' getUser = 'alice' некоторые другие столбцы 15/03/2018 14:49:22 [12834] [INFO] [alice] SyncCollections-> CheckForChanges (): Ожидание изменений магазина ... (время жизни 470 секунд) 15/03/2018 14:55:26 [12843] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:26 [12918] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:26 [12882] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:27 [12970] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:28 [12882] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:28 [12918] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:32 [12970] [INFO] [bob] method = 'POST' from = '192.168.0.166' getUser = 'bob' некоторые другие столбцы 15/03/2018 14:55:32 [12970] [INFO] [bob] SyncCollections-> CheckForChanges (): Ожидание изменений магазина ... (время жизни 470 секунд) [...] 

Цель

Я заинтересован в извлечении имени пользователя (в данном примере «Алиса» или «Боб») из файла журнала, который отображается в 5-м столбце, и соответствующего IP-адреса, который указан в 7-м столбце. В случае, если IP-адрес отличается от последнего состояния, уведомление по электронной почте должно быть отправлено с помощью небольшого сценария bash.

Условие должно быть:

  • если строка содержит «alice» ИЛИ «bob» И строка содержит «from =», выведите имя пользователя и соответствующий IP-адрес.

Конечный результат должен выглядеть

Боб 192.168.0.166 Алиса 192.168.0.171 

Примечание. Требуется только последний известный IP-адрес, поэтому в этом примере выходные данные должны генерировать только 2 строки, как показано выше (по одной для каждого пользователя).

Что я пробовал до сих пор

Я начал с awk, но быстро столкнулся с препятствием, потому что awk по умолчанию использует пробел в качестве разделителя полей. Я хотел начать с заявления . Я понял, что третий столбец иногда нарушает эту фильтрацию из-за пробела в id процесса, например

24.04.2017 20:14:50 [7910] ... 

Как выглядит моя команда awk в данный момент

С помощью следующей команды я ищу строку «alice» ИЛИ «bob» И строку «from =», а затем генерирую вывод двух неформатированных столбцов

awk 'BEGIN { FS = "[?!([ )]+" } /alice|bob/ && /from=/ { print $5,$7 }' test.log

Выход ->

боб] из = '192.168.0.163' боб] из = '192.168.0.163' боб] из = '192.168.0.163' боб] из = '192.168.0.163' боб] из = '192.168.0.151' боб] из = '192.168.0.151' боб] из = '192.168.0.151' боб] из = '192.168.0.151' боб] из = '192.168.0.163' боб] из = '192.168.0.163' боб] из = '192.168.0.151' боб] из = '192.168.0.151' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.153' Алиса] из = '192.168.0.171' Алиса] из = '192.168.0.171' боб] из = '192.168.0.166' боб] из = '192.168.0.166' боб] из = '192.168.0.166' боб] из = '192.168.0.166' боб] из = '192.168.0.166' боб] из = '192.168.0.166' боб] из = '192.168.0.166' 

Я застрял здесь. Я попытался поиграть, сохранив последнюю известную строку в переменной и выдав "", но, очевидно, я делаю что-то не так, потому что получаю либо ошибки, либо вывод неправильный. Моей следующей идеей было использовать «tac» и начать читать лог-файл с его конца и выйти после первого совпадения. Что-то вроде того:

tac test.txt | awk 'BEGIN / alice | bob / && / from = / ' 

но это сразу останавливается после 1-го матча и выводится:

боб] из = '192.168.0.166' 

Мне нужно дополнительно форматировать вывод, убрав правую скобку ']' и строку 'from =' и одинарные кавычки вокруг IP-адреса.

Любая помощь действительно ценится. Заранее спасибо.

0

2 ответа на вопрос

0
meuh

Вы можете расширить разделитель полей регулярных выражений, чтобы включить, ]и 'тогда вы будете иметь имя и ip без ошибок в полях 5 и 9. Вы можете сохранить их в ассоциативном массиве, проиндексированном по имени и содержащему последний IP-адрес. В конце файла вы печатаете этот массив.

awk 'BEGIN { FS = "[?!([ )\\]'\'']+" } /alice|bob/ && /from=/ {  user = $5; ip = $9; userip[user] = ip } END{ for(user in userip)print user,userip[user] }' 
0
user882786

Здравствуйте, meuh и большое спасибо за ваше предложение с примером. Это работает довольно хорошо. Но я все еще задаюсь вопросом, не было бы лучше отменить обработку и начать чтение с конца файла. Потому что в этом случае, если файл журнала, который нужно прочитать, имеет тысячи строк, он потребляет много вычислительной мощности. Я предполагаю, что было бы более эффективно в отношении производительности начинать чтение с хвоста и останавливаться после первого совпадения для каждого пользователя.

С другой стороны, мне интересно, возможно ли включить весь мой проект в awk в виде одной строки?

Цель состоит в том, чтобы запускать задание cron каждую минуту и ​​читать файл журнала. Если IP-адрес изменился и является более новым, чем последний известный, а подсеть ip не находится в пределах Subnet-C (LAN), то должно быть отправлено уведомление по электронной почте.

/etc/cron.d/access-audit.log

*/1 * * * * root nice -n5 /usr/bin/awk 'BEGIN { FS = "[?!([ )\]'\'']+" } /alice|bob/ && /from=/ { user = $5; ip = $9; userip[user] = ip } END{ for(user in userip)print user,userip[user] }' | ...

Я не знаю, как этого добиться. Нужно ли трогать файл флага, где я храню текущий IP-адрес каждого пользователя, а затем каким-то образом запрашивать его позже? Возможно ли сделать все в awk?