我有一个每分钟 4GB 大小的系统日志文件,下面是每分钟 2 行,共 450 万行,
我想生成一个只有几列的新文件eventtime|srcip|dstip
,因此结果如下
1548531299|X.X.X.X|X.X.X.X
请注意,字段的位置是随机的。
我尝试了一些过滤器,但在具有 4 个核心和 16GB RAM 的强大 VM 机器上处理一个文件仍然需要 40 多分钟。
那么有没有一种方法可以处理这么大的文件并快速过滤所需的列?
{Jan 26 22:35:00 172.20.23.148 date=2019-01-26 time=22:34:59 devname="ERB-03" devid="5KDTB18800169" logid="0000000011" type="traffic" subtype="forward" level="warning" vd="Users" eventtime=1548531299 srcip=X.X.X.X srcport=3XXXX srcintf="GGI-cer.405" srcintfrole="undefined" dstip=X.X.X.X dstport=XX dstintf="hh-BB.100" dstintfrole="undefined" sessionid=xxxxxxx proto=6 action="ip" policyid=5 policytype="policy" service="HTTP" appcat="unscanned" crscore=5 craction=xxxxxx crlevel="low"
Jan 26 22:35:00 172.20.23.148 date=2019-01-26 time=22:34:59 devname="XXX-XX-FGT-03" devid="XX-XXXXXXXX" logid="0000000013" type="traffic" subtype="forward" level="notice" vd="Users" eventtime=1548531299 srcip=X.X.X.X srcport=XXXXX srcintf="XXX-Core.123" srcintfrole="undefined" dstip=X.X.X.X dstport=XX dstintf="sXX-CC.100" dstintfrole="undefined" sessionid=1234567 cvdpkt=0 appcat="unscanned" crscore=5 craction=123456 crlevel="low"}
答案1
Perl 来救援
将以下脚本保存为filter.pl
并使其可执行(chmod +x
):
#!/usr/bin/env perl
use strict;
use warnings;
while( <> ) {
if ( /^(?=.*eventtime=(\S+))(?=.*srcip=(\S+))(?=.*dstip=(\S+)).*$/ ) {
print "$1|$2|$3\n";
}
}
然后运行
pduck@ubuntu:~> time ./filter.pl < input.txt > output.txt
real 0m44,984s
user 0m43,965s
sys 0m0,973s
正则表达式使用环视四周模式,在本例中为积极展望,以任意顺序匹配eventtime
、srcip
、 和三个值。dstip
我复制了你的两行输入,直到得到一个 4 GB 大小、约 900 万行的文件。我在 SSD 上运行了代码。
答案2
如果您想要一个真正快速的解决方案,我建议您使用 flex 工具。Flex 生成 C。以下内容能够处理像接受自由订单字段那样的示例。因此,创建一个文件,其名称f.fl
为以下内容:
%option main
%%
char e[100], s[100], d[100];
eventtime=[^ \n]* { strcpy(e,yytext+10); }
srcip=[^ \n]* { strcpy(s,yytext+6); }
dstip=[^ \n]* { strcpy(d,yytext+6); }
\n { if (e[0] && s[0] && d[0] )printf("%s|%s|%s\n",e,s,d);
e[0]=s[0]=d[0]=0 ;}
. {}
%%
测试尝试:
$ flex -f -o f.c f.fl
$ cc -O2 -o f f.c
$ ./f < input > output
比较如下time
:
$ time ./f < 13.5-milion-lines-3.9G-in-file > out-file
real 0m35.689s
user 0m34.705s
sys 0m0.908s
答案3
我将您的两行“输入”复制到一个文件大小为 3867148288 字节(3.7 GiB)的文件中,我可以grep
在 8 分 24 秒内处理它(从 HDD 读取和写入。使用 SSD 或 RAM 驱动器应该更快)。
为了尽量减少所用时间,我只使用了 的标准功能grep
,并且没有对其进行后期处理,因此输出格式不是您指定的格式,但无论如何可能都很有用。您可以测试此命令
time grep -oE -e 'eventtime=[0-9]* ' \
-e 'srcip=[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]' \
-e 'dstip=[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]' \
infile > outfile
两行输出:
$ cat outfile
eventtime=1548531298
srcip=X.X.X.Y
dstip=X.X.X.X
eventtime=1548531299
srcip=X.X.X.Z
dstip=X.X.X.Y
输出文件包含 25165824 行,对应输入文件中的 8388608(830 万)行。
$ wc -l outfile
25165824 outfile
$ <<< '25165824/3' bc
8388608
我的测试表明grep
每分钟可以处理大约 100 万行。
除非你的电脑比我的快得多。这还不够快,而且我认为你必须考虑一些快几倍的东西,可能在写入日志文件之前进行过滤,但是最好完全避免输出不必要的内容(并避免过滤)。
输入文件是通过复制生成的,也许系统会“记住”它之前见过相同的行,从而使处理速度更快,所以我不知道它在处理包含所有不可预测变化的真正大文件时会有多快。你必须测试它。
编辑 1:我在配备英特尔第四代 i7 处理器和 SSD 的 Dell M4800 上运行了同样的任务。它用了 4 分 36 秒完成,速度几乎翻倍,达到每分钟 182 万行。
$ <<< 'scale=2;25165824/3/(4*60+36)*60/10^6' bc
1.82
还是太慢了。
编辑2:我简化了grep
模式并在戴尔中再次运行它。
time grep -oE -e 'eventtime=[^\ ]*' \
-e 'srcip=[^\ ]*' \
-e 'dstip=[^\ ]*' \
infile > out
耗时 4 分 11 秒,略有提升,达到每分钟 200 万行
$ <<< 'scale=2;25165824/3/(4*60+11)*60/10^6' bc
2.00
编辑 3:@JJoao,perl 扩展速度高达grep
39 秒,相当于计算机每分钟 1290 万行,而普通grep
读取速度为每分钟 100 万行(从 HDD 读取和写入)。
$ time grep -oP '\b(eventtime|srcip|dstip)=\K\S+' infile >out-grep-JJoao
real 0m38,699s
user 0m31,466s
sys 0m2,843s
这个 perl 扩展是经验性的,info grep
但可以在我的 Lubuntu 18.04.1 LTS 中使用。
'-P' '--perl-regexp' 将模式解释为 Perl 兼容正则表达式 (PCRE)。这是实验性的,特别是与 '-z' ('--null-data') 选项结合使用时,并且 'grep -P' 可能会警告未实现的功能。*注意其他选项::。
我也按照@JJoao的flex
方法编译了一个C程序,它在53秒内完成,相当于计算机每分钟949万行,而普通grep
读取速度为每分钟100万行(从硬盘读取和写入)。 两种方法都很快,但grep
使用perl扩展是最快的。
$ time ./filt.jjouo < infile > out-flex-JJoao
real 0m53,440s
user 0m48,789s
sys 0m3,104s
编辑 3.1:在配备 SSD 的 Dell M4800 中,我得到了以下结果,
time ./filt.jjouo < infile > out-flex-JJoao
real 0m25,611s
user 0m24,794s
sys 0m0,592s
time grep -oP '\b(eventtime|srcip|dstip)=\K\S+' infile >out-grep-JJoao
real 0m18,375s
user 0m17,654s
sys 0m0,500s
这对应于
flex
应用程序每分钟 1966 万行grep
使用 perl 扩展每分钟 2735 万行
-f
编辑 3.2:在带有 SSD 的 Dell M4800 中,当我使用flex 预处理器选项时,我得到了以下结果,
flex -f -o filt.c filt.flex
cc -O2 -o filt.jjoao filt.c
结果得到了改善,现在flex
应用程序显示速度最高
flex -f ...
$ time ./filt.jjoao < infile > out-flex-JJoao
real 0m15,952s
user 0m15,318s
sys 0m0,628s
这对应于
- 该应用程序每分钟3155万行
flex
。
答案4
这是一个可能的解决方案,基于这个答案, 由...提供 @PerlDuck不久以前:
#!/bin/bash
while IFS= read -r LINE
do
if [[ ! -z ${LINE} ]]
then
eval $(echo "$LINE" | sed -e 's/\({\|}\)//g' -e 's/ /\n/g' | sed -ne '/=/p')
echo "$eventtime|$srcip|$dstip"
fi
done < "$1"
我不知道它在如此大的文件上会如何表现,在我看来,awk
解决方案会快得多。以下是它如何使用提供的输入文件示例:
$ ./script.sh in-file
1548531299|X.X.X.X|X.X.X.X
1548531299|X.X.X.X|X.X.X.X
time
以下是在配备 SSD 和 16GB RAM 的普通 i7 上进行的生产力测试的结果:
$ time ./script.sh 160000-lines-in-file > out-file
real 4m49.620s
user 6m15.875s
sys 1m50.254s