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.

  • Does the solution have to use grep? I imagine a solution in perl... – Jeff Schaller Feb 7 '16 at 23:27
0
grep -ozP  "(?s)(abc)[^(abc)]*(mno)" 1
abc
yyy
mno
abc
xxx
mno
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;
  }
}

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.