How to get the n-th line after a grepped one?
Consider the following text file
one 1
two 2
three 3
four 4
five 5
six 6
seven 7
eight 8
I would like to access to the second line after the one which matched four
. This would be the line
six 6
The resulting line (so the one above) would then be piped for further processing (say, a | cut -d' ' -f2
).
Is there a way to do this in bash and other typical utilities? (otherwise I will script it in Python)
EDIT: in my specific case the occurrence of four
(to take that example) is guaranteed unique. But the answers show interesting extended cases when it is not.
Solution 1:
There is nothing wrong with the previous two answers, but I thought I would make you aware that finding the third line after a pattern can be done in a single sed
call:
sed -n "/four/ { n; n; p }" SourceData.txt
Because a single program does the work, this is more efficient than running multiple filters. The above command outputs the third line after every instance of "four", except where this occurs again in one of the two lines following a match (the other solutions don't handle this case in the expected manner either); also, no output is generated if the pattern is in the last or second-last line of the file, which may or may not be what you want.
To match the first instance only:
sed -n "/four/ { n; n; p; q }" SourceData.txt
(Note that this answer is as efficient as possible, by ending the scan as soon as the match is found.)
I add this solution because it is worth getting to know sed
and, despite its rather off-putting syntax (regular expressions are bad enough!), it can often be extremely useful. This tutorial is a good introduction.
Solution 2:
Note: this answer was originally written before the OP clearly stated the pattern appears just once. It is designed not to miss any occurrence (unless near the end, so there's no "n-th line after") and I'm going to leave it this way. If you're sure there's only one occurrence or if you want only the first one to be found, you may consider some other solution that stops immediately and doesn't parse the whole input stream/file in vain.
This solution prints the current line iff there was a match two lines ago. It is slightly different from few other answers because it won't miss another match even if it occurs soon after the previous match.
awk -v delay=2 '{for (i=delay; i>=0; i--) t[i]=t[i-1]} /four/ {t[0]="m"} {if (t[delay]) print}'
Whenever there's a match, the information is stored in t[0]
. With each line the t
array is shifted (including shifting t[-1]
to t[0]
to reset the value of t[0]
). The line is printed iff the array indicates there was a match two lines ago.
You can easily set a different delay (e.g. delay=7
) or use another pattern (e.g. /sda[[:digit:]]/
)
Solution 3:
You can use this expression (input.txt
):
grep "four" -A 2 input.txt | tail -n 1
Output is:
six 6
The grep
option "-A 2" states that two lines after the matched line are outputted.
And the tail
option "-n 1" states that only the last 1
lines of this result are returned.