8

How can I "grep" the lines between an occurrence of some string1 and the (Nth) occurrence of some string2.

e.g.

if the file has line:

A
B
C
D
E

F
G
B
C
E

Q

I want to get the lines in bold (those that begin with a B and end with an E).

Can this be done using grep? or some other Unix command line tool?

1

2 Answers 2

13

grep is not well suited for this task, you need to go one tool "up":

sed -n '/^B/,/^E/p' infile

Output:

B
C
D
E
B
C
E

With regards to the Nth requirement, I think its easiest if you again advance one tool "up", namely awk:

awk '/^B/ { f = 1; n++ } f && n == wanted; /^E/ { f = 0 }' wanted=2 infile

Output:

B
C
E

The flag f will be set when /^B/ is encountered and unset when /^E/ occurs, much in the same way the sed notation works. n keeps a tally of how many blocks have passed and when f == 1 && n == wanted is true, the default block will be executed ({ print $0 }).

4
  • Can you please explain what exactly does it do? I understand that send goes over an input stream, I am guessing the syntax here means something like (don't print)[-n] but print [p] everything from a regexp /^B/ to [,] regexp /^E/ ?
    – epeleg
    Apr 11, 2013 at 13:38
  • Is there a way to inject some separator between those two found blocks?
    – epeleg
    Apr 11, 2013 at 13:42
  • @epeleg: the sed example works as you assume, all command following the range are executed whilst the input is in that range. Its a bit complicated to insert separators with sed, I would rather use awk instead, see the latest edit.
    – Thor
    Apr 11, 2013 at 15:31
  • You can add a separator with sed like this: /^B/,/^E/ { /^E/G; p; }
    – Thor
    Nov 6, 2019 at 11:43
3

@Thor's sed command cannot be beaten, but with the following perl script I try to address the part of your question in parenthesis: "... the (Nth) occurrence ...".

Usage:

./script <start-regex> <end-regex> [N]

Examples with the file in your question:

$ ./script "B" "E" < examplefile
B
C
D
E
B
C
E

$ ./script "B" "E" 2 < examplefile
B
C
D
E
F
G
B
C
E

There is no error checking or whatsoever and the script is non-greedy, i.e. from A B C D E E F only B C D E will be grep'ed with N=1.


#!/usr/bin/perl

if ($ARGV[2] != "") { $n = $ARGV[2] } else { $n = 1 }
$begin_str = $ARGV[0];
$end_str = $ARGV[1];

while(<STDIN>) {
  if($_ =~ $begin_str) { $flag=1 }             # beginning of match, set flag    
  if($_ =~ $end_str && $flag eq 1) { $i++ }    # i-th occurence of end string

  if($i eq $n) {                               # end of match after n occurences of end string
    $flag=2;
    $i=0; 
  }

  if ($flag ge 1) {                            # append currrent line to matching part
    $out.=$_;
  }

  if($flag eq 2) {                             # after detection of end of match, print complete match
    print $out;
    # print "---\n";                           # separator after a match
    $out="";
    $flag=0;
  }

}
5
  • Thanks. I might use this as well in the future, but for now I believe the sed solution is my way to go.
    – epeleg
    Apr 11, 2013 at 13:43
  • 1
    looking at this code again, I really like it because I can modify it to suit my exact needs. (like maybe appending the iteration number at the beginning of each line for example).
    – epeleg
    Apr 11, 2013 at 13:53
  • can you explain why you are concatenating to $out instead of just printing line by line?
    – epeleg
    Apr 11, 2013 at 13:57
  • @epeleg: You can't print line-by-line (I had that at first, too), because then the sequence A B D will result in B D, although there is no "end terminator" E. Apart from that, this gives you an easy way to put in a "separator between those two found blocks" (I included that as a comment in the script now).
    – mpy
    Apr 11, 2013 at 14:11
  • Thinking about it again, even though you are absolutely right about what you said, for my needs its ok and even desired to get the B D in your sample. so I will go with direct printing (saves all the string concatenation).
    – epeleg
    Apr 15, 2013 at 6:29

You must log in to answer this question.

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