![zgrep 在多个文件中性能缓慢](https://linux22.com/image/174032/zgrep%20%E5%9C%A8%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B8%AD%E6%80%A7%E8%83%BD%E7%BC%93%E6%85%A2.png)
我有一个9.8GBgzip 文件 A.gz 和我拥有的其他文件是79MBB.txt 每行都有一些文本。我想在 A.gz 中 grep B 的文本并写入一个新文件。
最初,我使用这个命令
zgrep -f B.txt A.gz > C.xml
但这个命令被挂起,并在很长一段时间内创建了一个空的 C.xml。
然后在谷歌搜索后我了解到,因为 B.txt 很大,所以当它将文本保留在缓冲区中时,它会挂起。
所以我将文本文件分成每个 20000 个文本
split -l 20000 -a 4 B.txt B
我创建了像 Baaaa、Baaab 这样的文件......
然后迭代每个文件
cd B
for f in B*; do
zgrep -f "$f" ../A.gz >> C.xml
done
它非常慢并且仍在运行。
对此有更好的方法吗?
压缩 gz 文件会提高性能吗?
更新
我尝试使用-F
zgrep -F -f "$f" ../A.gz >> C.xml
这有点快,但仍然想要其他选择
我有像这样的xml
<root>
<source>source1</source>
<Id>123</Id>
<category>ABC</category>
</root>
<root>
<source>source2</source>
<Id>123</Id>
<category>XYZ</category>
</root>
这里的 id 是相同的 123 但类别不同 ABC 和 XYZ
(输入是有限的类别集,例如 ABC、DEF、GHI、JKLM、NOP)最初我的类别为 ABC,因此根据类别 ABC,我找到它的 id,即 123,这样我继续写属于这些的所有 id将类别输入到新文件,即 B.txt(id 列表),如下所示
zgrep -E 'ABC|DEF|GHI|JKLM|NOP' A.gz | sed -n 's:.*<Id>\(.*\)</Id>.*:\1:p' | uniq > B.txt
稍后我迭代这个 id 并获取所有 xml,这样我就获得了属于 id 123 的类别 ABC 和 XYZ 的 xml 标签
答案1
79MB 的 grep“字符串”使用起来会很痛苦。这些行是B.txt
真正的正则表达式还是固定的相同字符串?如果它们是固定字符串,它们在A.gz
整行中是否显示相同?未压缩中的多少行A.gz
预计与 中的行匹配B.txt
?
模式匹配建议
如果中的行B.txt
确实是正则表达式或行的子字符串,A.gz
您可能会被迫使用类似的东西超扫描它旨在处理巨大的正则表达式。如果您有足够的磁盘空间,您可以解压缩A.gz
,然后让 HyperScan 开始工作(您甚至可以让 shell 在 HyperScan 搜索时即时解压缩)。另一种尝试的替代方法是ripgrep。
全线搭配建议
如果您正在处理固定的全行字符串,B.txt
并且未压缩的字符串A.gz
包含相对较小(假设为 100MB 左右)的匹配行,那么编写一个程序进行预处理可能会更好A.gz
:
- 您可以对每一行进行散列
B.txt
并记住散列 - 然后,您检查未压缩哈希值中的任何行是否
A.gz
与之前的任何哈希值相同。如果是这样,您打印出该行(例如进入C.txt
)以准备进一步处理 - 现在,您将进行最后一次检查,更严格地检查每一行是否在
B.txt
其中C.txt
(反之亦然 - 取决于哪个文件较小)
进行初始近似过滤的一些代码可能是这样的:
# Do a quick APPROXIMATE filter of lines in FILENEEDLES that are also in
# FILEHAYSTACK
import sys
def main():
if len(sys.argv) < 2:
print("usage: %s FILENEEDLES FILEHAYSTACK" % sys.argv[0])
exit(1)
first_filename = sys.argv[1]
second_filename = sys.argv[2]
line_hashes = set()
with open(first_filename, "r") as f:
for line in f:
line_hashes.add(hash(line))
with open(second_filename, "r") as f:
for line in f:
if hash(line) in line_hashes:
sys.stdout.write(line)
if __name__ == "__main__":
main()
例如:
$ echo -e '1\n2\n3' > B.txt
$ echo -e '2\n3\n4\5' | gzip > A.gz
$ ./approxfilter.py B.txt <(gzip -dc A.gz) > candidates.txt
$ cat candidates.txt
2
3
您现在需要检查candidates.txt以查看行输出是否完全匹配B.txt
(但这希望是一个更小且更容易的问题,如果候选行的数量“小”,您甚至可以修改上面的程序来完成这一切“并且完全在内存中可以保存的范围内)。 (提问者后来在评论中澄清,他们不使用全行长度字符串,因此这种方法不起作用)
答案2
您的第二次尝试很可能通过解压缩得到改进,否则循环的每次迭代都将具有完整的解压缩开销 - 提前解压缩将意味着您只有一次该开销。
如果这仍然不够快,您也可以尝试多线程(假设解压 A)。
find B -type f -name 'B*' -print0 \
| xargs -0 -t -n1 -P8 \
grep -f {} A >> C.xml
此示例应同时运行 8 个进程,您可能需要根据您拥有的处理器/核心数量调整此值。
我不确定您期望的速度结果是什么样的;坦率地说,听起来你给了它很多工作要做,而很多工作都需要时间。
答案3
zgrep
只是 的 shell 脚本包装器grep
。它只是运行grep
您系统上安装的任何内容并用于gzip
解压缩输入文件。
如果您使用的是 GNU grep 版本 3.5 或 3.6,最近发现了一个错误,该错误似乎会减慢模式文件的处理速度,正如您所描述的,性能极度下降。
grep 3.7 发行说明中的错误示例模式文件大约有 48 Mb 的模式,因此据我所知,大小应该不是问题。