Printing Lines between two Two Pattern

Viewed 80

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.

5 Answers

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
$ 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
...

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?.

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

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.

Related