有什么有效且快速的方法可以在日志文件中捕获两个匹配项?
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 说:
- 如果我们正在读取的行的第一个字段(该字段由 awk 默认的 space 字符分隔)以
nnnn-nn-nn
格式的日期开始:~/^[0-9]{4}-[0-9]{2}-[0-9]{2}/
- 然后打印整行
{print [=17=]}
- 如果我们正在读取的行的第一个字段以字符
(mail=>
开头:~/^\(mail=>/
- 然后将第一个字段按字符
<
或 >
拆分为名为 a
的数组:split(,a,"[<>]")
- 然后打印数组中的第 3 项(索引 2):
print a[2]
- 对于当前目录中的所有文件:
*
- 不是打印到命令行,而是将输出发送到文件:
> 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
我想得到一些想法。
我的情况:我的 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 说:
- 如果我们正在读取的行的第一个字段(该字段由 awk 默认的 space 字符分隔)以
nnnn-nn-nn
格式的日期开始:~/^[0-9]{4}-[0-9]{2}-[0-9]{2}/
- 然后打印整行
{print [=17=]}
- 如果我们正在读取的行的第一个字段以字符
(mail=>
开头:~/^\(mail=>/
- 然后将第一个字段按字符
<
或>
拆分为名为a
的数组:split(,a,"[<>]")
- 然后打印数组中的第 3 项(索引 2):
print a[2]
- 对于当前目录中的所有文件:
*
- 不是打印到命令行,而是将输出发送到文件:
> 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