5

I need to grep for a pattern and once I find it, I need to look for another pattern preceding that and print all those lines in between those.

Better example:

1 a
2 a
3 a
4 a
5 a
6 b
7 c
8 d
9 xyz
10 xyz
11 a
12 a

Grep for xyz and then grep for first a preceding xyz line and print all the lines.

Output (first preceding occurrence of a, I don;t care about other a's in the file):

5 a 
6 b
7 c
8 d
9  xyz
10 xyz
  • What OS and what version of grep? – iruvar Jul 25 '14 at 22:02
  • Is what you want is to eliminate duplicates @user1413813 – Networker Jul 25 '14 at 22:06
  • No i dont want to remove duplicates. i want to get the first occurrence of "8000-1 blah test1" preceding "xyz" and then print the lines between those (including those lines) – user1413813 Jul 25 '14 at 22:10
  • 1
    You show two “xyz” lines in the input, and you show both of them in the output. Please describe the behavior you want. Do you want the output to continue to the last “xyz” in the file? E.g., if lines 9-12 were “xyz”, “foo”, “xyz”, “bar”, would you want the output to be lines 5-11? I ask because you say, “I need to grep for a pattern (“xyz”) and once I find (it), I need to look for another pattern (“a”) preceding that …”, which sounds like you want find the first “xyz”. Also, what if lines 9-14 were “xyz”, “b”, “a”, “r”, “xyz”, “foo”? Would you want lines 5-13 or just 11-13? – Scott Jul 26 '14 at 0:04
1

Here's a solution in Perl:

perl -nlE '
    if    (/a/)   { @buffer = ($_) }
    elsif (/xyz/) { push @buffer,$_; say for @buffer }
    else          { push @buffer,$_}
' your_file

How this works

It reads through the file line-by-line and does one of three things:

  1. If the current line matches the pattern a, it assigns the current line to the @buffer array.
  2. If the current line matches the pattern xyz, it pushes the current line onto the buffer and prints the contents of the buffer
  3. If none of the two cases above is true, it simply appends the current line to the @buffer array.

Thus, whenever a new line matches the pattern a, the contents of the @buffer are erased and replaced by the current line only. This guarantees you will find the closest a preceding xyz.

You should of course replace the regexes I used with the actual regexes relevant to your case.

1

I think you could do something like

grep -zPo '.*a(?s)(?(?!a).)*?xyz' file

(with GNU grep, if your version is recent enough to support the PCRE extension) or

pcregrep -Mo '.*a(?s)(?(?!a).)*?xyz' file

If you want to match to the second instance of xyz in your output, make the match greedy by removing the final ? e.g. grep -zPo '.*a(?s)(?(?!a).)*xyz' file.


Perhaps a better test is this, with multi-character patterns for both the start and stop condition; given

$ cat file
1 abc    
2 abc
3 abc
4 abc
5 abc
6 bbc
7 cbc
8 dbc
9 xyz
10 xyz
11 abc
12 abc

then

$ grep -zPo '.*abc(?s)(?(?!abc).)*?xyz' file
5 abc
6 bbc
7 cbc
8 dbc
9 xyz
1
tac file | sed '/xyz/,/a/!d' | tac

(if you don't have tac, your tail command may have a -r command to do the same thing).

0

If you don't care blank lines in output, try:

$ awk '
    !NF { next }
    /a/ { flag = 1; last = $0; next }
    last && flag { print last; print; last = 0; next}
    /xyz/ { print; flag = 0; next}
    flag
' file
5 a
6 b
7 c
8 d
9 xyz
10 xyz

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.