n=$some_num
{ head -n"$(($(wc -l <in)-n))" >/dev/null
grep 'match your string'
} <in
Unfortunately this requires reading the file entirely through w/ wc
to get a line-count because it's not clear otherwise how many lines are in the file or how large $n
is. That aside, this should be a very performant solution provided <in
is a regular, lseek()
able file.
So first we get our line count and subtract $n
from it. head
reads in that many lines from stdin and writes the results to /dev/null
. What remains afterward are $n
-count lines of input on stdin and just your grep
and your pattern.
Technically this does cheat - there is a pipe in the command substitution for wc
. It is my hope that you can overlook that.
By the way, another way to do this could look like:
{ grep "-m$n" 'some pattern near yours' >/dev/null
grep 'your pattern'
} <in
...with a GNU grep
. If you can grep
$n
occurrences of another pattern which will get you in the neighborhood of your targeted pattern, then you might truly do it without a pipe at all.
I tried to stick w/ grep
, but here is a sed
solution anyway. The pipes below are just for input - and grep
is not involved at all excpet to prepend line numbers so you can see which numbers they are. All of that is only for the example case. You can use the sed
script alone with a named file or stdin of any kind and set $pat
and $n
appropriately and it will work.
I actually just rewrote this because I didn't like not being able to anchor the matches. This is a little slower - not noticeably, and it is still very fast, but for each buffered tail line it trims all of trailing pattern space and isolates the first line in the buffer. In this way all of the normal anchor expressions work as expected.
pat=man n=40
man man |
grep -n ''|
sed -e:B -e'${/^\n/D' \
-eh -e's/\n.*//' \
-e"/$pat/p;x" \
-e\} -e'$D;N;$bB' \
-e"$n,$ D;bB"
648: /etc/man_db.conf
649: man-db configuration file.
651: /usr/share/man
652: A global manual page hierarchy.
654: /usr/share/man/index.(bt|db|dir|pag)
657: /var/cache/man/index.(bt|db|dir|pag)
661: apropos(1), groff(1), less(1), manpath(1), nroff(1), troff(1), whatis(1),
662: zsoelim(1), setlocale(3), manpath(5), ascii(7), latin1(7), man(7), cat-
663: man(8), mandb(8), the man-db package manual, FSSTND
680: developing and maintaining man-db.
Here is another example, but on a file:
pat=. n=15
seq 100 >nums
sed -e:B -e'${/^\n/D' \
-eh -e's/\n.*//' \
-e"/$pat/p;x" \
-e\} -e'$D;N;$bB' \
<nums -e"$n,$ D;bB"
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
n
lines and (2)grep
for a pattern in those lines is a perfect fit for 2 commands connected with a pipe.