The problem is: print the line n lines before or after a line that matches a given pattern. How to do this in awk?
The sample input
Here is a sample input file:
line1 foobar line3 line4 line5 foobar line7 foobar line8
We want to look for matches of "foobar", and print the lines 1, 2 or 3 lines before or after the matching lines.
n lines after the match
This can be accomplished by maintaining a "queue" (implemented with an array) of the line numbers of the future lines to be printed, updated every time a match is found (ok, it's not a proper queue, but it has the same logical function). This uses the "two files" awk idiom, despite there being a single input file:
$ awk -v n=1 '/foobar/{queue[NR+n]} NR in queue' file.txt line3 line7 line8 $ awk -v n=2 '/foobar/{queue[NR+n]} NR in queue' file.txt line4 foobar $ awk -v n=3 '/foobar/{queue[NR+n]} NR in queue' file.txt line5 line8
To avoid keeping already-used keys in the array, they can of course be deleted, although this should be hardly needed (thanks igli from #awk):
$ awk -v n=5 '/foobar/{queue[NR+n]} NR in queue {print; delete queue[NR]}' file.txt
n lines before the match
This is slightly more complicated. Basically, we need to maintain a "sliding window" of n lines, and when a match is found, print the line NR-n lines before (which will be the first line in the sliding window). Modular arithmetic comes handy because using NR%n always gives a number from 0 to n-1, which can be used to access the sliding window array.
$ awk -v n=1 '/foobar/ && NR>n {print window[(NR-n)%n]}{window[NR%n]=$0}' file.txt line1 line5 line7 $ awk -v n=2 '/foobar/ && NR>n {print window[(NR-n)%n]}{window[NR%n]=$0}' file.txt line4 foobar $ awk -v n=3 '/foobar/ && NR>n {print window[(NR-n)%n]}{window[NR%n]=$0}' file.txt line3 line5
We also need to check that NR is greater than n because otherwise we may print lines before the first, which obviously does not make sense.
very useful for pulling dates of events out of Oracle alert log