9

I want to search for a particular string in a certain file. If I found the string, I also want to print the line X lines before (or after) that line.

Can this with grep or awk, or do I need a combination?

I would like to have something like this, but not with all trailing/leading lines before or after a hit, only the Xth.

For example, if my input looks like:

line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
...

I e.g. want to search for the word 'pattern' and output that line + the line that is 2 lines after that, but not the line that directly follows the line with the pattern. So the desired output is:

line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern
...
3
  • awk is so general purpose that you can use it to solve your task but you would have to write a script. So, in this case the answer would be yes. As you already found out grep can handle your task to a certain extent but maybe not all of it. Why do you focus on these two tools but mention sed in your tags? Is your real question: Can I do this only using/combining standard GNU tools without scripting? The answer is also probably yes, too, but may be a little more tricky because of special cases: What e.g. is the desired output if the pattern is found in the first X or last X lines? Dec 14, 2013 at 13:15
  • @MarcusRickert Yes, the real question is if I can do this with GNU tools without scripting. Concerning the first or last lines: the output is consistent, so if a pattern appears, I'm sure the needed X lines will follow. I will add an example to the question.
    – MaVe
    Dec 14, 2013 at 17:36
  • See stackoverflow.com/questions/9081/…
    – Vadzim
    Jul 15, 2014 at 13:33

4 Answers 4

16

grep will do this for you, with options -A (after) and -B (before), and -C (context). An example I often use is:

   sudo lspci -vnn | grep -i net -A 12

because this command will show 12 lines after the match, which includes the driver used to control the network (-i net) card. In general, the command is:

  grep text_to_search -A n -B m file.extension

which will output m lines before the match, and n lines after the match. Or you can use

 grep text_to_search -C n file.extension

to show a total of n lines surrounding the text found (half before, half after the match).

1
  • 1
    Your post (upvoted) is mostly correct, except that the -C n flag is not as you state. -C n is equivalent to -A n -B n and shows n lines of context on either side of the match.
    – Jim L.
    Feb 21, 2019 at 6:15
3

After looking into this question a little bit I think I have to revise my comment about the feasability using standard GNU tools without scripting. This may be very hard to do because of the special cases.

If you don't mind using awk I could offer the following solution. This script context.awk ist still rather concise:

{
    lines[NR] = $0
    if (dump[NR]) {
        print $0;
    if ($0 ~ Pattern) {
        if (NR-Delta in lines) {
            print "---"
            print lines[NR-Delta]
        }
        dump[NR+Delta] = 1
        print $0;
    }
    if (NR-Delta in lines) 
        delete lines [NR-Delta];
}

It has to be called as follows:

awk -v Delta=X -v Pattern=PATTERN -f context.awk sample.txt

where X is the desired "context distance" and PATTERN the search pattern. The script tries to seperate multiple pattern contexts in the file by printing lines with --- in between. So, for example, the following sample.txt

line1
line2
line3 XXX
line4
line5
line6
line7 XXX
line8
line9
line10 XXX
line11

using this call

awk -v Delta=3 -v Pattern=XXX -f context.awk sample.txt

will yield the followng output

line3 XXX
line6
---
line4
line7 XXX
line10 XXX
---
line7 XXX
line10 XXX
1
  • Of course, the script could also be integrated into the call if awk. Dec 16, 2013 at 6:37
2

The following sed command seems to work for the case you describe which I would restate as: print the line matching my pattern, do not print the immediately following line, also print the line two lines after my pattern, then keep searching for pattern matches.

sed -n -e '/with a pattern/ {h;n;n;H;x;p}' file

It seems a bit ugly, but by adding additional n (skip line, move to the next one) and H (keep this line by appending it to the hold buffer) commands you can construct an arbitrary keep/skip relationship following a pattern match.

Note that following the match we initially copy the matching line to the hold buffer with the h command. Finally we swap the hold and pattern space with x and print the pattern space with p. At this point sed will continue processing the file line-by-line looking for another match.

A handy reference for sed commands can be found here.

0

grep -A2 <pattern> file | grep -B1 <pattern> | grep -v "\-\-" works for me:

user@box /tmp $ grep -A2 "with a pattern" test.txt | grep -B1 "with a pattern" | grep -v "\-\-"
line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern

user@box /tmp $ cat test.txt 
line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .