有什么有效且快速的方法可以在日志文件中捕获两个匹配项?

Is there any effective & fast way to catch two match in a log file?

我想得到一些想法。

我的情况:我的 Linux 服务器上有大量日志,它们很大,而且里面也有很多东西。我只想从日志中捕获带有时间戳的登录名和电子邮件地址,并将它们收集到一个 .txt 文件中。

示例日志:

[...]
2019-07-21 03:13:06.939 login 
[things not needed between the two]
(mail=>example@mail.com< method=>email< cmd=>login<)
[...]

示例输出:

************** 2019-07-21 **************
2019-07-21 03:13:06.939 login
example@mail.com
2019-07-21 06:22:19.424 login
example@mail.com
2019-07-21 12:10:23.665 login
example@mail.com
2019-07-21 14:26:19.068 login
example@mail.com

************** 2019-07-22 **************
2019-07-22 08:01:50.157 login
example@mail.com
2019-07-22 08:12:35.504 login
example@mail.com
2019-07-22 09:10:35.416 login
example@mail.com

为了实现这一点,我现在正在使用它:

for i in $(ls); do echo "" && printf "************** " && cat $i | head -c 10 && printf " **************\n"; while read line; do echo $line | grep "login"; echo "$line" | grep -h -o -P '(?<=mail=>).*?(?=<)'; done < $i; done >> ../logins.txt

for 循环遍历文件,cat $i | head -c 10 将获取日期(因为这是每个日志中的第一件事),while 循环逐行读取文件并 greps 登录并且只有邮件地址(grep 在“mail=>”“<”之间)。最后它输出到 logins.txt.

虽然它正在运行,但我发现它非常非常慢,因为它正在执行大量命令。 (我们在这里谈论的是 2 年的日志)它看起来也很脏。

我真的认为有一种有效的方法可以做到这一点,但我真的不明白那会是什么。

awk 会做得很好。您可以告诉它仅当该行与特定正则表达式匹配时才打印该行。类似于:

 awk '[=10=]~/[0-9]{4}-[0-9]{2}-[0-9]{2}|\(mail=>/{print [=10=]}' * > output.log 

更新:注意到您只需要电子邮件。在这种情况下,两个块就足够了。在第二个块中,我们按字符 <> 拆分,然后从结果数组的索引 2 检索电子邮件。

 awk '~/^[0-9]{4}-[0-9]{2}-[0-9]{2}/{print [=11=]}~/^\(mail=>/{split(,a,"[<>]");print a[2]}' * > output.log

这个 awk 说:

  1. 如果我们正在读取的行的第一个字段(该字段由 awk 默认的 space 字符分隔)以 nnnn-nn-nn 格式的日期开始:~/^[0-9]{4}-[0-9]{2}-[0-9]{2}/
  2. 然后打印整行{print [=17=]}
  3. 如果我们正在读取的行的第一个字段以字符 (mail=> 开头:~/^\(mail=>/
  4. 然后将第一个字段按字符 <> 拆分为名为 a 的数组:split(,a,"[<>]")
  5. 然后打印数组中的第 3 项(索引 2):print a[2]
  6. 对于当前目录中的所有文件:*
  7. 不是打印到命令行,而是将输出发送到文件:> output.log

使用 awk 使用 -F 选择邮件帐户:

sep='************************'
awk -v sep="$sep" -F '(mail=>|<)' '
  FNR==1 { printf("%s %s %s\n", sep, substr([=10=],0,10), sep)}
  /mail=>/ {print }
  /login *$/ {print}
' *

当您有额外的要求并希望使用循环时,请考虑

for f in *; do
  sed -nr '
    1s/(.{10}).*/*********  **********/p;
    /login *$/p;
    s/.*mail=>([^<]*).*//p
  ' "${f}"
done