8

Using grep, you can print lines that match your search query. Adding a -C option will print two lines of surrounding context, like this:

> grep -C 2 'lorem'
some context
some other context
**lorem ipsum**
another line
yet another line

Similarly, you can use grep -B 2 or grep -A 2 to print matching lines with two preceding or two following lines, respectively, for example:

> grep -A 2 'lorem'
**lorem ipsum**
another line
yet another line

Is it possible to skip the matching line and only print the context? Specifically, I would like to only print the line that is exactly 2 lines above a match, like this:

> <some magic command>
some context
Inian
  • 80,270
  • 14
  • 142
  • 161
szaman
  • 2,159
  • 1
  • 14
  • 30
  • Not in `grep` that I am aware of. Use `awk` like in this: [printing with sed or awk a line following a matching pattern](http://stackoverflow.com/a/17914105/1983854) – fedorqui Jul 07 '16 at 11:06
  • @fedorqui this works for the following lines, but can i modify it to get the preceding lines? never used awk before. – szaman Jul 07 '16 at 11:12
  • 1
    Use `tac` to print the file backwards. – fedorqui Jul 07 '16 at 11:13
  • Can you then use `-v` to exclude the match line? – Karl Jul 07 '16 at 13:29
  • If you can allow two instances of `grep` to be used, you can try `grep -v "lorem" < <(grep -A2 "lorem" file)` – Inian Jul 07 '16 at 13:33

3 Answers3

4

If you can allow couple of grep instances to be used, you can try like as I mentioned in the comments section.

$ grep -v "lorem" < <(grep -A2 "lorem" file)
another line
yet another line

$ grep -A2 "lorem" file | grep -v "lorem"
another line
yet another line

If you are interested in a dose of awk, there is a cool way to do it as

$ awk -v count=2 '{a[++i]=$0;}/lorem/{for(j=NR-count;j<NR;j++)print a[j];}' file
another line
yet another line

It works by storing the entire file in its own array and after searching for the pattern lorem, the awk special variable which stores the row number(NR), points at the exact line in which the pattern is found. If we loop for 2 lines before it as dictated by the awk variable -v count, we can print the lines needed.

If you are interested in the printing the pattern also, just change the condition in for-loop as j<=NR instead of j<NR. That's it!

Inian
  • 80,270
  • 14
  • 142
  • 161
1

There’s no way to do this purely through a grep command. If there’s only one instance of lorem in the text, you could pipe the output through head.

grep -B2 lorem t | head -1

If there may be multiple occurrence of lorem, you could use awk:

awk '{second_previous=previous; previous=current_line; current_line=$0}; /lorem/ { print second_previous; }'

This awk command saves each line (along with the previous and the one before that) in variables so when it encounters a line containing lorem, it prints the second last line. If lorem happens to occur in the first or second line of the input, nothing would be printed.

Anthony Geoghegan
  • 11,533
  • 5
  • 49
  • 56
1

awk, as others have said, is your friend here. You don't need complex loops or arrays or other junk, though; basic patterns suffice.

When you use -B N, (and the --no-group-separator flag) you get output in groups of M=N+1 lines. To select precisely one of those lines (in your question, you want the very first of the group), you can use modular arithmetic (tested with GNU awk).

awk -vm=3 -vx=1 'NR%m==x{print}'

You can think of the lines being numbered like this: they count up until you reach the match, at which point they go back to zero. So set m to N+1 and x to the line you want to extract.

1 some context
2 some other context
0 **lorem ipsum**

So the final command would be

grep -B2 --no-group-separator lorem $input | awk -vm=3 -vx=1 'NR%m==x{print}'
wirefox
  • 444
  • 12
  • 15