Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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?

Thanks.

share|improve this question
    
This sounds as a tast for sed. However my sed-fu is not as good as Nicole's so I am just pointing at an close match. superuser.com/questions/513393/converting-strings-in-input-file –  Hennes Apr 11 '13 at 11:50
add comment

2 Answers

up vote 7 down vote accepted

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 }).

share|improve this answer
    
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 '13 at 13:38
    
Is there a way to inject some separator between those two found blocks? –  epeleg Apr 11 '13 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 '13 at 15:31
add comment

@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;
  }

}
share|improve this answer
    
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 '13 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 '13 at 13:53
    
can you explain why you are concatenating to $out instead of just printing line by line? –  epeleg Apr 11 '13 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 '13 at 14:11
    
good point about the "no end terminator". thanks. –  epeleg Apr 14 '13 at 8:49
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.