1

I have a file like this:

text1
text2
Pattern1
text3
text4
Pattern1
text5
text2
text6
Pattern1
text7
text8
Pattern2
text2
text9
text10
text2
Pattern1
text11
text12
Pattern1
text13
text14
Pattern1
text15
text16
Pattern2
text1
text2
Pattern1
text3
text4
Pattern1
text5
text6
Pattern1
text7
text2
text8
Pattern2
text21
text22

I want to print ONLY

Pattern1
text7
text8
Pattern2
Pattern1
text15
text16
Pattern2
Pattern1
text7
text2
text8
Pattern2

i.e. if Pattern1 is followed by Pattern2, then print lines between Pattern1 and Pattern2. I don't want lines if Pattern1 is followed by Pattern1.

An awk command is highly appreciated.

I was trying like:

awk '/Pattern1/,/Pattern2' file \> output

but it gives all the lanes that appear between two successive Pattern1 also.

RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93
  • https://unix.stackexchange.com/questions/331742/grep-from-the-last-occurrence-of-a-pattern-to-another-pattern – tripleee Mar 24 '22 at 10:52

5 Answers5

1

You may use this awk:

awk '
/^Pattern1$/ {s = $0 ORS; next}
s != "" {s = s $0 ORS}
/^Pattern2$/ {printf "%s", s; s = ""}
' file

Pattern1
text7
text8
Pattern2
Pattern1
text15
text16
Pattern2
Pattern1
text7
text2
text8
Pattern2
anubhava
  • 761,203
  • 64
  • 569
  • 643
1
$ awk '
/Pattern1/,/Pattern2/ {   # between the first start and end markers
    if(/Pattern1/)        # at the begin marker...
        b=$0              # ... reset buffer
    else 
        b=b ORS $0        # or keep on buffering
    if(/Pattern2/)        # at the end marker...
        print b           # ... it is time to output
}' file

head -4 of output:

Pattern1
text7
text8
Pattern2
...
James Brown
  • 36,089
  • 7
  • 43
  • 59
  • 1
    That need to duplicate the start/end "patterns" is why it's best to avoid range expressions, see [is-a-start-end-range-expression-ever-useful-in-awk](https://stackoverflow.com/questions/23934486/is-a-start-end-range-expression-ever-useful-in-awk). – Ed Morton Mar 24 '22 at 14:57
  • @EdMorton My point was to highlight why the provided sample solution as such was not enough to solve the problem. And yes, each such case is unique and `/../,/../` kind of makes a rotten stepping stone for each of them - try it, fail with it then do it like it should've been done from the beginning. – James Brown Mar 24 '22 at 15:08
1

Making a whole lot of assumptions about unstated requirements like whether to match to the first or last Pattern2, what to do if one or the other is missing from a file,what to do if both "patterns" occur on 1 line, etc.:

$ cat tst.awk
/Pattern1/ { f=1; rec="" }
f {
    rec = rec $0 ORS
    if ( /Pattern2/ ) {
        printf "%s", rec
        f = 0
    }
}

$ awk -f tst.awk file
Pattern1
text7
text8
Pattern2
Pattern1
text15
text16
Pattern2
Pattern1
text7
text2
text8
Pattern2

See also How do I find the text that matches a pattern?.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

If you just want to do it in one gulp, you can use perl:

perl -0777 -nE 'say $1 while (/(^Pattern1$
        (?:\R(?!^Pattern2$|^Pattern1$).*)*\R
    ^Pattern2$)/gmx)' file
Pattern1
text7
text8
Pattern2
Pattern1
text15
text16
Pattern2
Pattern1
text7
text2
text8
Pattern2
dawg
  • 98,345
  • 23
  • 131
  • 206
0

This might work for you (GNU sed):

sed -n 'H;/Pattern1/h;/Pattern2/{g;p}' file

Turn off implicit printing by setting -n.

Append the current line to the hold space (spare buffer).

If the current line contains Pattern1, replace the hold space by this line.

If the current line contains Pattern2, replace the current line by contents of the hold space and print the result.

potong
  • 55,640
  • 6
  • 51
  • 83