命令行 最快的筛选文件的方法

分享于 

10分钟阅读

ubuntu

 

问题:

每分钟有一个大小为 4GB的syslog文件,下面是每分钟10行的行,

我想生成一个只包含几列 eventtime|srcip|dstip的新文件,因此结果如下


1548531299|X.X.X.X|X.X.X.X



请注意,字段的位置是随机的。
我试过一些过滤器,但是在功能强大的虚拟机上处理一个文件的时间超过了 40分钟,拥有 4个内核和 16GB 个内存。

因此,有一种方法可以处理这样的大文件,并在快速方法中筛选所需的列?


{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|$3n";


 }


}



然后运行


pduck@ubuntu:~> time./filter.pl <input.txt> output.txt



real 0m44,984s


user 0m43,965s


sys 0m0,973s



在这种情况下,正规表达式 使用一个 环视 Pattern,在这个例子中是一个积极的前瞻性 lookahead,以任意顺序对三。

我复制了你的两条输入线直到得到了 4 GB和约 9万行的文件。 我在一个硬盘上。


回答 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|%sn",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 ),我可以在 8分钟和 24秒内处理 grep ( 读写)。 使用SSD或者ramdrive应该更快。

为了最小化使用的时间,我只使用了 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



输出文件中包含与输入文件中 8388608 ( 百万) 行对应的25165824行。


$ wc -l outfile


25165824 outfile


$ < <<'25165824/3' bc


8388608



测试表明 grep 每分钟可以处理大约 1条线路。

除非你的电脑比我的快。 这种方法不够快,我认为你需要考虑几次,在写日志文件之前,也许过滤一下,但是最好是完全避免输出不必要的数据。

输入文件是复制的,也可以能是系统'记忆'以前看到的行,因这里我不知道它有多快。 你得测试一下。

Edit1: 我在戴尔M4800中运行同样的任务,使用英特尔 4th 代处理器和一个 SSD。 它以 4分钟和 36秒完成,几乎是双倍速度,每分钟 1.82行。


$ < <<'scale=2;25165824/3/(4*60+36)*60/10^6' bc


1.82



还是太慢。

Edit2: 我简化了 grep 模式,并再次在戴尔中运行。


time grep -oE -e 'eventtime=[^ ]*' 


 -e 'srcip=[^ ]*' 


 -e 'dstip=[^ ]*' 


infile> out



在 4分钟和 11秒之后完成,每分钟 2.00 lines行的小改进


$ < <<'scale=2;25165824/3/(4*60+11)*60/10^6' bc


2.00



编辑 3: 在计算机中,@JJoao's, perl扩展将每秒对应的每秒 39万行数据加速,其中普通 grep 每分钟读取一百万行行。


$ time grep -oP 'b(eventtime|srcip|dstip)=KS+' infile> out-grep-JJoao



real 0m38,699s


user 0m31,466s


sys 0m2,843s



这个perl扩展根据 info grep 是 experiental,但它在我的Lubuntu 18.04.1.

'-p''--perl-regexp'将 Pattern 解释为一个兼容的正则表达式( PCRE )。 这是实验性的,特别是在与'-z'('--null-data') 选项结合使用时,'。-P'可能会警告未实现的特性。 *Note 其他选项:。

根据 @JJoao's flex 方法编写了一个C 程序,在计算机中每分钟对应 9.49行,其中普通 grep 每秒读取两百万行代码。 这两种方法都很快,但是 grep 具有perl扩展。


$ time./filt.jjouo <infile> out-flex-JJoao



real 0m53,440s


user 0m48,789s


sys 0m3,104s



编辑 3.1: 在带有SSD的戴尔M4800中,我得到了以下结果


time./filt.jjouo <infile> out-flex-JJoao



real 0m25,611s


user 0m24,794s


sys 0m0,592s



time grep -oP 'b(eventtime|srcip|dstip)=KS+' infile> out-grep-JJoao



real 0m18,375s


user 0m17,654s


sys 0m0,500s



这对应于

  • flex 应用程序每分钟的19.66 百万行
  • 使用 27.35 扩展的grep 每分钟的百万行

编辑 3.2: 在戴尔M4800中使用了以下结果,当我使用选项 -f 到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



这对应于

  • flex 应用程序每分钟的31.55 百万行。

回答 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"



我不知道它在这么大的文件上会表现怎样,IMO的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:


$ time./script.sh 160000-lines-in-file> out-file



real 4m49.620s


user 6m15.875s


sys 1m50.254s





COM  文件  COMM  fast  Comma  命令行