Print lines between 1st match and 2nd match, 2nd match that came after the 1st match in Linux

132 Views Asked by At

I have a series of data I want to print the data after one match and next different match which comes after the first match for example,

I have

AAA 
AAAD
BBB 
CCC
AAA
AAADD

Want the data between AAAD and AAA.

There's AAA in the 1st line too but I want it to catch AAAD first and then print the data till it catches the next match which is AAA on line 5

output needed :

AAAD
BBB
CCC
AAA

I tried

perl -lne 'print if /AAAD/../AAA/' p

it gave

AAAD
AAADD

it's giving exact matches and printing them rather than giving lines in between.

5

There are 5 best solutions below

2
Bork On

As noted in comments, you need to use the ... range operator to prevent matching on the same line. Like so:

C:\perl>perl -lnwe "print if /AAAD/../AAA/" a.txt
AAAD
AAADD

C:\perl>perl -lnwe "print if /AAAD/.../AAA/" a.txt
AAAD
BBB
CCC
AAA
AAADD

From the documentation:

If you don't want it to test the right operand until the next evaluation, as in sed, just use three dots ("...") instead of two. In all other regards, "..." behaves just like ".." does.

This code, with the regexes you provided, allows partial matches. If this is not what you want, you can use anchors to enforce only complete matches. E.g.:

C:\perl>perl -lnwe "print if /^AAAD$/.../^AAA$/" a.txt
AAAD
BBB
CCC
AAA
0
Ed Morton On

This will work for the one sunny-day scenario you describe:

$ awk '$0=="AAAD"{f=1} f; $0=="AAA"{f=0}' file
AAAD
BBB
CCC
AAA

but, just like any other answer you get at this point, it may not be the right solution for you depending on your requirements for rainy day scenarios, including some I listed in my comment.

I'm using literal string instead of regexp matching so it works even if your matching strings contain regexp metachars. See How do I find the text that matches a pattern? for more info.

Though awk supports range expression such as /^AAAD$/,/^AAA$/ or $0=="AAAD",$0=="AAA" I used a flag f instead because range expressions require a total rewrite or duplicate code if you want to do anything even slightly different with your script in future, e.g. not print the start and/or end line of the matching block or not print from the line that matches the starting pattern to the end of the file if the ending pattern is not matched. See Is a /start/,/end/ range expression ever useful in awk? for more info.

0
ikegami On
perl -ne'print if /^AAAD$/ .. /^AAA$/'

Or if you just want the first:

perl -ne'print if $x ||= /^AAAD$/; exit if $x && /^AAA$/;'

This was your attempt:

perl -ne'print if /AAAD/ .. /AAA/'

It's close. The issue is that the string AAAD matches /AAA/. We can fix that using anchors

perl -ne'print if /^AAAD$/ .. /^AAA$/'
0
karakfa On
$ awk '/^AAAD$/,/^AAA$/' file

AAAD
BBB
CCC
AAA

same with sed

$ sed -n '/^AAAD$/,/^AAA$/p' file
0
potong On

This might work for you (GNU sed):

sed -n '/^AAAD$/{:a;N;/^AAA$/M!ba;p}' file

This is a filter operation so employ the -n option to prevent implicit printing.

Match on a line only containing AAAD and then gather up future lines until one containing only AAA.

Print the collection and repeat.

All other lines will quietly disappear.

N.B. The range operation behaves in a flip-flop manner and is not suitable if both start and end conditions must be met i.e. if a line containing only AAAD is matched any command following the range will be activated until a line containing only AAA or the end of the file.