我有一个数千个文本文件(大约 148,000 行长),其中包含很多这样的序列:
b 29.
b 52.
c 84.
c 83.
c 94.
c 93.
c 61.
b 38.
c 81.
c 92.
c 28.
c 37.
c 27.
...由于文件太大,我希望能够搜索这样的模式(非功能性单行):
grep "b\ 34.\nc53.\nb\ 54.\na\ 45.\nd\ 44.\nd\ 63.\nd\ 64.\n" 文件名
看来 awk 是一个不错的选择。
我怎样才能做到这一点,并打印匹配的行号?
答案1
这是一个搜索多行字符串的 awk 脚本(匹配必须由整行组成)。它接收要在变量中搜索的文本needle
。该脚本的工作原理是构建一个行窗口w
(其中w
是 中的行数needle
)并将其与 进行比较needle
。
awk -v needle='b 38.\nc 81.\nc 92.\n' '
BEGIN {
if (substr(needle, length(needle)) == "\n")
needle = substr(needle, 1, length(needle)-1);
w = split(needle, needles, "\n");
getline window
for (i = 2; i < w; i++) {getline; window = window "\n" $0}
}
{ window = window "\n" $0 }
window == needle {print NR - w + 1}
{ window = substr(window, index(window, "\n") + 1) }
' <data.txt
这不是搜索子字符串的最有效方法,因为数据文件中的每一行都会与模式中的每一行进行比较。有更有效的算法可以通过在模式中进行一些预计算来设法执行更少的比较,例如高德莫里斯普拉特。
对于适合内存的文件,我会一次读取所有内容并在内存中执行搜索。如果您要寻找的只是模式匹配,这在 Perl 中很容易完成,但 Perl 缺乏有效跟踪行的原语。下面是一个查找多行字符串(必须按原样传递)的 Python 脚本。
import re, sys
needle = sys.argv[1]
haystack = sys.stdin.read()
pos = 0
line = 1
for m in re.finditer(needle, haystack):
line += haystack.count("\n", pos, m.start())
pos = m.start()
print line
用法:python -c '…' $'b 38.\nc 81.\nc 92.\n' <data.txt
答案2
我会这样写:
awk -v seven_lines="b 34.c 53.b 54.a 45.d 44.d 63.d 64." '
seven_lines == l6 l5 l4 l3 l2 l1 $0 {print "pattern found at line " (NR-6)}
{l6=l5; l5=l4; l4=l3; l3=l2; l2=l1; l1=$0}
'