tags:

views:

362

answers:

6

I need to search for a pattern and write that line as well as the next 3 lines into a file (FILE). Is this a correct way to do it? Thanks.

print FILE if /^abc/;
$n=3 if /^abc/;
print FILE if ($n-- > 0);
+5  A: 

You could simplify it to using a flag variable to know if you should print a line:

while( <$input> ) {
    $n=4 if /^abc/; 
    print FILE if ($n-- > 0);
    }

Besides simplification, it also fixes a problem: in your version the abc string will be printed twice.

Igor Krivokon
ITYM while, not if.
Sinan Ünür
No, I meant if, while is not needed here.
Igor Krivokon
@Igor OK, so those two lines are meant to be within a while loop. However, $n will be decremented every time through the loop. I guess it could wrap around if you had enough lines between the ones that start with abc ;-)
Sinan Ünür
Thanks - this works!
Bi
A: 

I'd rather take a few extra lines of code and make everything more clear. Something like this should work:

my $count = 0;
while ( my $line = pop @file ) {
   if ( /^abc/ ) {
      $count = 4;
   }

   if ( $count > 0 ) {
       print FILE $line;
       $count--;
   }
}

Edit to respond to comments:

  • missing the regex was a bug, fixed now.
  • printing the newlines or not is certainly optional, as is slurping the file in. Different people have different styles, that's one of the things that people like about Perl!
James Thompson
Two minor potential problems: you probably don't need to insert "\n". Also, if one of the 3 lines after abc contains abc, you will miss it.
Igor Krivokon
No need to slurp the input.
Sinan Ünür
+1  A: 

There is no need to slurp the file in or try to write your code on a single line:

#!/usr/bin/perl
use strict;
use warnings;

while ( my $line = <DATA> ) {
    if ( $line =~ /^abc/ ) {
        print $line;
        print scalar <DATA> for 1 .. 3;
    }
}
__DATA__
x
y
z
abc
1
2
3
4
5
6
Sinan Ünür
This has a bug, given the file "abc\nabc\n1\n2\n\n" you will print the first four lines, but not the fifth.
Chas. Owens
+5  A: 

This is a feature of the command-line grep(1). No programming needed:

grep abc --after-context=3

You do get '--' lines between groups of context, but those are easy enough to strip. It's also easy enough to do the whole thing in Perl. :)

The trick is what you want to do when one of the following three lines also contains the pattern you're looking for. grep(1) will reset the counter and keep printing lines.

brian d foy
It's a feature of GNU grep - not of POSIX grep.
Jonathan Leffler
+2  A: 

I like .. operator:

perl -ne 'print if (/abc/ and $n=3) .. not $n--'

but you doesn't have described what should happen if abc pattern is repeated in following three lines. If you want restart counter, your approach is correct if fix a little bug with double print.

perl -ne'$n=4 if/abc/;print if$n-->0'
Hynek -Pichi- Vychodil
A: 

Another possible solution...

#!/usr/bin/perl

use strict;

my $count = 0;
while (<DATA>) {
    $count = 1 if /abc/;
    if ($count >= 1 and $count <= 3) {
        next if /abc/;
        print;
        $count++;
    }
}

__DATA__
test
alpha
abc
1
234123
new_Data
test
bichonfrise74