Here's an awk script that searches a multiline string (matches must consist of whole lines). It receives the text to search for in the variable needle
. The script works by building a window of w
lines (where w
is the number of lines in needle
) and comparing that against 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
This is not the most efficient way to search for a substring, because each line in the data file is compared with each line in the pattern. There are more efficient algorithms that manage to perform fewer comparisons by making some precomputations in the pattern, such as Knuth-Morris-Pratt.
For a file that fits comfortably in memory, I would read it all at once and perform the search in memory. If all you're looking for is a pattern match, this is easily done in Perl, but Perl lacks primitives to efficiently keep track of lines. Here's a Python script that looks for a multiline string (which must be passed as such).
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
Usage: python -c '…' $'b 38.\nc 81.\nc 92.\n' <data.txt