有一个非常大的文本文件,其中有两个逗号分隔的值:
78414387,10033
78769989,12668
78771319,13677
78771340,13759
80367563,16336
81634533,10025
82878571,10196
110059366,10218
110059411,10812
110059451,10067
我需要在日志文件中搜索这些值,如下所示:
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c737e3d80d7def296c7| id=278832702| version=28| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c732f18c26fe604fd04| id=284057302| version=9| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c747e3d80d7def296c8| id=357229| version=1151| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c742f18c26fe604fd05| id=279832706| version=35| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c744697ddb976cf5a95| id=354171| version=503| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:53 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c754697ddb976cf5a96| id=355638| version=1287| timestamp=1467432053000
我的脚本:
#!/bin/bash
COUNT=0
while IFS=',' read ID VERSION; do
VERSION=`echo $VERSION |col -bx`
if (grep "id=${ID}| version=$VERSION" worker-6715.log.2016-$1.log.* > /dev/null); then
let "COUNT++"
else
echo "$ID, $VERSION FAIL"
exit 2
fi
done < recon.txt
echo "All OK, $COUNT checked"
- 如果我从日志文件中删除不必要的字段,这会加快执行速度吗?
- 如果我创建一个 RAM 设备并将日志文件复制到那里,这会加快执行速度还是我的红帽Linux 6(Hedwig) 无论如何缓存文件?还有其他建议吗?
答案1
您的瓶颈是recon.txt
在 shell 中逐行读取文件。要获得失败的行,您可以预处理日志中的行,使其看起来像 中的行recon.txt
,然后用于comm(1)
查找设置的差异,可能如下所示:
comm -23 \
<(sort -u recon.txt) \
<(sed 's/.*| id=\([0-9]*\)| version=\([0-9]*\)|.*/\1,\2/' worker-6715.log.2016-$1.log.* | \
sort -u)
这假设有一个可以处理构造的 shell <(...)
。另请注意,结果中的行不保留 中行的顺序recon.txt
。保持这个顺序会更困难(而且更慢)。
如果您还需要成功计数,则可以反其道而行之,进行预处理,recon.txt
使其看起来像在日志中找到的内容,然后使用fgrep(1)
或grep -F
进行搜索。将区域设置设置为C
还可以在某些系统上显着加快速度。因此:
COUNT=$( \
sed 's/\([0-9]*\),\([0-9]*\)/| id=\1| version=\2|/' recon.txt | \
LC_ALL=C fgrep -f - worker-6715.log.2016-$1.log.* | \
wc -l )
这假设recon.txt
不包含重复项,并且每行在recon.txt
所有日志中最多匹配一次。取消第一个限制将很困难。第二个可以通过仔细选择来解除comm(1)
。
答案2
正如所指出的,这里的主要问题是 shell 循环。我会awk
首先处理日志文件,连接id
和version
值并将结果保存到数组中,然后读取recon.txt
并在每一行上检查它是否在数组中,如果不是 - 将行内容保存在变量中t
并exit
立即(执行END
块)。在块中,如果thenEND
中保存了一行带有消息,否则只打印 OK 消息,但没有。的行数:t
exit 2
recon.txt
awk 'NR==FNR{j=$9","$10;gsub(/[^0-9,]/, "", j);a[j]++;next}
!($0 in a){t=$0;{exit}}
END{if (t){print t, "FAIL"; exit 2}
else{print "All OK,", FNR, "checked"}}' logfile recon.txt
id
这假设和的数值version
分别位于第 9 和第 10 字段。如果不是这种情况,您可以使用正则表达式,例如佐藤确实 - 也就是说,如果你的awk
风格支持反向引用,否则类似这样的事情会发生:
NR==FNR{sub(/.* id=/, "");sub(/\| version=/, ",");sub(/\|.*/, ""));a[$0]++;next}
答案3
我将这些文件放入数据库(任何你喜欢的类型)。也许甚至不导入大文件,而是从生成该输出的应用程序中逐行放入。
这样DB引擎就不会浪费资源每次调用grep而是使用C编译的内部函数。
不要忘记在导入后(如果导入整个文件)或创建表后(如果逐行插入记录)创建索引。
答案4
首先将 recon.txt 转换为 grep -F 可以快速使用的格式:
perl -pe 's/,/| version=/' recon.txt > recon.grep-F
那么你的情况就如上面所描述的https://www.gnu.org/software/parallel/parallel_examples.html#example-grepping-n-lines-for-m-regular-expressions
这里的文字适合您的问题:
grep 大量正则表达式的大文件的最简单解决方案是:
grep -f regexps.txt bigfile
或者如果正则表达式是固定字符串:
grep -F -f recon.grep-F worker.log
有 2 个限制因素:CPU 和磁盘 I/O。 CPU 很容易测量:如果 grep 占用 >90% CPU(例如,运行 top 时),那么 CPU 是一个限制因素,并行化将加快这一速度。如果不是,则磁盘 I/O 是限制因素,并且根据磁盘系统,并行化可能会更快或更慢。唯一确定的方法就是测量。
如果 CPU 是限制因素,则应在正则表达式上进行并行化:
cat recon.grep-F | parallel --pipe -L1000 --round-robin grep -F -f - worker.log
如果一行与多个正则表达式匹配,则该行可能会重复。该命令将为每个 CPU 启动一个 grep 并为每个 CPU 读取一次大文件,但由于这是并行完成的,除了第一个读取之外的所有读取都将缓存在 RAM 中。根据 regexp.txt 的大小,使用 --block 10m 而不是 -L1000 可能会更快。如果 regexp.txt 太大而无法放入 RAM,请删除 --round-robin 并调整 -L1000。这将导致bigfile被读取更多次。
一些存储系统在并行读取多个块时性能更好。对于某些 RAID 系统和某些网络文件系统来说确实如此。并行读取大文件:
parallel --pipepart --block 100M -a worker.log -k grep -F -f recon.grep-F
这会将 bigfile 分割成 100MB 的块,并对每个块运行 grep。要并行读取 bigfile 和 regexp.txt,请使用 --fifo 将两者结合起来:
parallel --pipepart --block 100M -a worker.log --fifo cat recon.grep-F \
\| parallel --pipe -L1000 --round-robin grep -f - {}
如果一行与多个正则表达式匹配,则该行可能会重复。