原来是我grep
昨天不小心用错了。我刚刚检查了 bash 历史记录并看到了我正在执行的内容:
grep search-string-here -f large-file-with-long-lines.txt
这就是导致内存耗尽的原因。
执行:
grep search-string-here large-file-with-long-lines.txt
...具有所需的行为。
感谢@αГsнιη指出了具有类似错误的问题,并感谢@EdMorton和@ilkkachu纠正了我对行长度以及如何grep
使用awk
内存的假设。
下面是最初的问题(尽管我对长线无法容纳 8 GB RAM 的看法是错误的)和 @EdMorton 接受的答案。
我有一个非常大的文件(超过 100 GB),其中包含很长的行(甚至无法放入 8 GB RAM),我想在其中搜索字符串。我知道grep
做不到,因为grep
试图将整行放入内存中。
到目前为止,我想出的最好的解决方案是:
awk '/search-string-here/{print "Found."}' large-file-with-long-lines.txt
我实际上对这个解决方案很满意,但我只是想知道是否有一些更直观的方法来做到这一点。也许有其他一些实现grep
?
答案1
这是一个简单的部分解决方案,仅当有一个字符未出现在要搜索的字符串(或正则表达式)中时才有效,但是在线上经常出现以便该字符出现之间的间隔始终适合内存。例如,假设每一行都是一个非常长的列表,由相对较短的分号分隔字段组成。
<large-file-with-long-lines.txt tr ';' '\n' | grep 'search-string-here'
这是一个不同的部分解决方案,如果出现的次数总是从行首之后的 N 个字符的倍数处开始,对于一些固定的 N。它使用fold
换行和ag
进行多行搜索。在此示例中,已知出现的位置始终在行首之后的 3*x 个字符处开始。
<large-file-with-long-lines.txt fold -w3 | ag $'cat\ntag\ngag\nact'
通过重复搜索每个偏移量,这可以推广到任意字符串搜索。
<large-file-with-long-lines.txt fold -w3 | ag $'fee\n-fi\n-fo\n-fu\nm|fe\ne-f\ni-f\no-f\num|f\nee-\nfi-\nfo-\nfum'
请注意,如果字符串碰巧几乎存在,但中间有换行符,则可能会出现误报。
答案2
这会很慢,因为它会多次读取输入文件(您想要查找的字符串中的每个字符一次,每次在字符串中读取该搜索字符串的大小),但它应该适用于任何大小的文件和任何大小的行,使用 GNU awk 进行多字符RS
和RT
(未经测试):
awk -v str='search-string-here' '
BEGIN {
lgth = length(str)
RS = sprintf("%*s",lgth,"")
gsub(/ /,".",RS)
for (i=1; i<lgth; i++) {
ARGV[ARGC++] = ARGV[1]
}
}
RT == str {
print "found"
exit
}
' file
它将 RS 设置为 N .
s,其中 N 是搜索字符串的长度,从字符 #1 开始读取输入中的每一个 N 个字符链,将它们与搜索字符串进行比较,如果输入中的当前 N 个字符与搜索匹配,则退出细绳。如果该遍没有匹配,它会再次执行相同的操作,但从 char #2 开始,依此类推,直到完成 N 次,因此输入文件中不再有长度为 N 的字符串可与搜索字符串进行比较。
请注意,上面是进行字符串比较,而不是正则表达式比较。要进行正则表达式比较,您必须以其他方式确定匹配字符串的最大长度,然后使用正则表达式比较运算符~
而不是==
,例如,如果您知道输入中的匹配字符串不能超过 20 个字符,则你可以这样做:
awk -v regexp='search-regexp-here' '
BEGIN {
lgth = 20
RS = sprintf("%*s",lgth,"")
gsub(/ /,".",RS)
for (i=1; i<lgth; i++) {
ARGV[ARGC++] = ARGV[1]
}
}
RT ~ regexp {
print "found"
exit
}
' file
但是正则表达式搜索有一些字符串搜索不需要考虑的缺陷,例如,如果您的搜索正则表达式包含^
或$
作为边界,那么您可能会得到上面的错误匹配,因为它会在每个字符周围创建字符串开始/结束边界当读取 N 个字符长的字符串时。