0

I have an input like below. I want to find out everything starting with abc and ending with mno including lines in between but if abc appears again before mno comes, then I want to ignore the first matched abc. The idea is, I just need a group which starts with abc and ends with mno which are nearest to each other.

test.txt file contains below data:

abc
bbb
abc
yyy
mno
abc
xxx
mno

Expected output :

abc
yyy
mno
abc
xxx
mno

I am using the below grep liner:

grep -ozP  "(?s)(abc).\*?(mno)" test.txt

The result is:

abc
bbb
abc
yyy
mno
abc
xxx
mno

The first two lines should not be there in the output. Please advise what I can modify in grep to get the desired result.

1
  • Does the solution have to use grep? I imagine a solution in perl...
    – Jeff Schaller
    Commented Feb 7, 2016 at 23:27

3 Answers 3

1

One way to go about this is to reverse the file using tac, find matches starting with mno and ending with abc, and reverse that to get the desired result. I got the following to work:

$ tac test.txt | pcregrep -M 'mno(\n|.)*?abc' | tac
abc
yyy
mno
abc
xxx
mno

(I'm using pcregrep for the multiline -M flag)

1

Just in case perl is an option for you:

#!/usr/bin/env perl
# saved lines to print out
my @out = ();
# should we save lines?
my $saving = 0;
while (<>) {
  if (/abc/) {
    if ($saving) {
      # this is the second /abc/, so dump what we were saving and start over
      @out = ($_);
    } else {
      # this is the first /abc/, so save it and start saving lines
      push @out, $_;
      $saving = 1;
    }
  } elsif (/mno/) {
    if ($saving) {
      # print what we've saved, plus this /mno/ ending line, then reset
      print @out, $_;
      @out=();
      $saving=0;
    }
  } else {
    # otherwise, save lines if we should be
    push @out, $_ if $saving;
  }
}
0
grep -ozP  "(?s)(abc)[^(abc)]*(mno)" 1
abc
yyy
mno
abc
xxx
mno

You must log in to answer this question.

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