Sam
Sam

Reputation: 43

How to grab multiple lines after matching a line in Perl?

My file looks like this:

1  15
2  16
3  18
4  19
5  25
6  30
7  55
8  45
9  34
10 52

If the matched pattern is 30 in line 6, I would like to grab N lines before and M lines after the line 6, for example if N=3 and M=4 so the result is expected to be like this:

3  18
4  19
5  25
6  30
7  55
8  45
9  34
10 52

I am a very new beginner in Perl and any advice would be appreciated.

﹟UPDATE Many thanks for these helpful advice below and I really appreciate them. Here is my updated code for this and any suggestions are welcome!

my $num;

while(<>)
{   
if ( /pattern/) 
{$num = $. ;}   
}

open (,"") || die ("Can't open the file");

while(<>)

{
if (  $. >= $num-N and $. <=$num+M)
{    
print OUT "$_ \r";
}
}

Upvotes: 0

Views: 1439

Answers (2)

Mattan
Mattan

Reputation: 753

my @lines = <>;
foreach $idx (grep { $lines[$_] =~ /pattern/ } 0..$#lines) {
    print join (map {$lines[$_]} grep { $_ >= $idx - $B && $_ <= $idx +$A } 0..$#lines)."\n";
}

You can also use the GNU grep command, with -A,-B flags for that exact purpose.

  -A NUM, --after-context=NUM
          Print NUM lines  of  trailing  context  after  matching  lines.
          Places  a  line  containing  --  between  contiguous  groups of
          matches.

   -B NUM, --before-context=NUM
          Print  NUM  lines  of  leading  context  before matching lines.
          Places a  line  containing  --  between  contiguous  groups  of
          matches.

Upvotes: 0

Kyle Strand
Kyle Strand

Reputation: 16519

Maintain an array (I'll call it @preceding) of the last N lines read. When the pattern is matched, stop updating this array and start inserting lines into another array (@following). Do this until @following has M lines in it.

It should look something like this (fixed now thanks to ikegami):

my $matched = 0;
my @preceding;
my @following;
while(<>){
    if ($matched){
        push ( @following, $_);
        last if @following == M;
        next;
    }
    else {
        push ( @preceding, $_);
        shift(@preceding) if @preceding > N;
    }
    $matched = 1 if /pattern/;
}

Upvotes: 1

Related Questions