如何在 awk 中解析多行日志文件并仅输出带有最后一个已知 IP 地址的单行

如何在 awk 中解析多行日志文件并仅输出带有最后一个已知 IP 地址的单行

我陷入困境,正在寻求帮助。我想触发一个事件,并希望通过 Bash 脚本进一步处理该事件。数据是从日志文件中检索的。在开始解释之前,我将向您展示该特定日志文件的几行,以便您更好地理解。

外观

测试日志

[...]
2017 年 4 月 24 日 20:14:29 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:14:34 [10355] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:14:38 [10355] [INFO] [bob] 已处理“1”个传入更改
2017 年 4 月 24 日 20:14:47 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:14:50 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:14:53 [ 7910] [INFO] [bob] 已处理‘1’个传入更改
2017 年 4 月 24 日 20:15:08 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:14 [22518] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:15 [ 7910] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:16 [10355] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:49 [32637] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:53 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:15:56 [22518] [INFO] [bob] 已处理‘1’个传入更改
2017 年 4 月 24 日 20:16:05 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' 一些其他列
2017 年 4 月 24 日 20:16:09 [32637] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' 一些其他列
2017 年 1 月 5 日 03:27:45 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2017 年 1 月 5 日 03:27:49 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2017 年 1 月 5 日 03:28:05 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2017 年 1 月 5 日 03:28:10 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2017 年 1 月 5 日 03:28:25 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2017 年 1 月 5 日 03:28:31 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' 一些其他列
2018 年 3 月 15 日 14:49:19 [12918] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' 一些其他列
2018 年 3 月 15 日 14:49:21 [12834] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' 一些其他列
2018 年 3 月 15 日 14:49:22 [12834] [INFO] [alice] SyncCollections->CheckForChanges():正在等待存储更改...(生命周期 470 秒)
2018 年 3 月 15 日 14:55:26 [12843] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:26 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:26 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:27 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:28 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:28 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:32 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' 一些其他列
2018 年 3 月 15 日 14:55:32 [12970] [INFO] [bob] SyncCollections->CheckForChanges():正在等待存储更改...(生命周期 470 秒)
[...]

目标

我感兴趣的是从日志文件中检索第 5 列中显示的用户名(本例中为“alice”或“bob”)以及第 7 列中列出的相应 IP 地址。如果 IP 地址与上次状态不同,则应通过小型 bash 脚本发送电子邮件通知。

条件应该是:

  • 如果该行包含“alice”或者“鲍勃”该行包含“from=”,然后输出用户名和适当的IP地址。

最终输出应如下所示

鲍勃 192.168.0.166
爱丽丝 192.168.0.171

笔记:由于只需要最后一个已知的 IP 地址,因此正确的输出应该只生成如上所示的本例中的 2 行(每个用户一行)

我迄今为止尝试过

我开始awk但很快就遇到了一个障碍,因为 awk 默认使用空格作为字段分隔符。我原本打算从 '{ print $4,$6 }' 语句开始。我意识到第三列有时会因为进程 ID 中的前导空格而破坏这种过滤,例如

2017 年 4 月 24 日 20:14:50 [ 7910] ...

我的 awk 命令目前是什么样子

使用以下命令我搜索字符串“alice”或“bob”和字符串“from=”,然后生成两个未格式化的列的输出

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

输出 -->

bob]来自='192.168.0.163'
bob]来自='192.168.0.163'
bob]来自='192.168.0.163'
bob]来自='192.168.0.163'
bob]来自='192.168.0.151'
bob]来自='192.168.0.151'
bob]来自='192.168.0.151'
bob]来自='192.168.0.151'
bob]来自='192.168.0.163'
bob]来自='192.168.0.163'
bob]来自='192.168.0.151'
bob]来自='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'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'
bob]来自='192.168.0.166'

我被困在这里了。我尝试将最后一行存储到变量中并输出“{a=$0}”,但显然我做错了,因为我得到了错误或输出是错误的。我的下一个想法是使用“tac”并从末尾开始读取日志文件并在第一个匹配后退出。类似这样的事情:

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

但这在第一场比赛后立即停止,输出为:

bob]来自='192.168.0.166'

我需要另外通过删除右括号“]”和字符串“from =”以及 IP 地址周围的单引号来进行输出格式化。

非常感谢您的帮助。提前致谢。

答案1

您可以扩展正则表达式字段分隔符以包含]和,'然后您将在字段 5 和 9 中清晰地获得名称和 IP。您可以将它们保存在由名称索引的关联数组中,并保存最后一个 IP 地址。在文件末尾打印此数组。

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

答案2

你好,非常感谢你给出的示例建议。这个方法很好用。但我还是想知道,如果反转处理并从文件末尾开始读取,是不是更好。因为在这种情况下,如果要读取的日志文件有数千行,则会消耗大量的处理能力。我猜,从性能角度来看,从尾部开始读取并在每个用户第一次匹配后停止会更有效。

另一方面,我想知道是否可以将我的整个项目作为一行程序包含到 awk 中。

目标是每分钟运行一次 cron 作业并读取日志文件。如果 IP 地址发生变化且比上次已知的 IP 地址更新,并且 IP 子网不在子网 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 中完成所有操作?

相关内容