http://www.perlmonks.org?node_id=189653

If you've ever wanted to grep through files for a match, and print the lines following the matching line, and your system's grep doesn't do that, here's something that should do the job. Of course it uses perl's regex syntax for the matching pattern.
#!/usr/bin/perl # # Grep for something and display the line and # the N lines following the match (default 1) # Prefix each line with the filename # if there is more than 1 file argument # Exit status: # 0 - a match was found # 1 - No match was found # >1 - An error was encountered (even if a match was found) use Getopt::Std; $usage_str = <<EOT; Usage: ngrep [-h] [-i] [-n #] [-q] pattern file1 file2 ... -h - Suppress file names when multiple file arguments are supplied -i - Do case-insensitive search -n - Number of lines to display after match (default: 1) -q - Treat pattern as a literal string instead of a regex -r - Don't reset count if line following matched line matches EOT getopts('qrhin:') or die $usage_str; die $usage_str unless @ARGV; $pttrn = shift; $pttrn = quotemeta $pttrn if $opt_q; $pttrn = '(?i)' . $pttrn if $opt_i; $pttrn = eval { qr/$pttrn/ } or die "$@$usage_str"; $n = length($opt_n) ? int($opt_n)+1 : 2; $filename = (@ARGV > 1 and ! $opt_h) ? sub { "$ARGV: " } : sub { '' }; $SIG{__WARN__} = sub { warn $_[0]; $error = 2 }; $status = 1; $i=0; while (<>) { ($status, $i) = (0, $n) if ! ($opt_r and $i) and /$pttrn/; $i--, print &$filename, $_ if $i; $i = 0 if eof; } exit($error || $status);

Replies are listed 'Best First'.
•Re: Grep - print matched line and next N lines
by merlyn (Sage) on Aug 13, 2002 at 04:36 UTC
    I think your basic logic is overkill and prone to initial bugs, or maintenance bugs, because you're reading in two places, and testing in two places, and generally saying things twice. That's always a bad sign. Just do this:
    $n = 0; while (<>) { $n = 7 if /$pattern/; # start printing if ($n) { print; $n-- } # print if within $n = 0 if eof; # don't leak across file boundaries }
    Plus or minus a few parameters, of course.

    -- Randal L. Schwartz, Perl hacker

      Well, I'm all for making things simpler, so I've updated it with your suggestion (and keeping all the previous parameters intact). I was a little concerned about testing for eof on every iteration, but it doesn't really make that big of a difference. And I think doing it this way makes it easier to add more options, like setting different 'after' counts for different patterns and such.
Re: Grep - print matched line and next N lines
by Anonymous Monk on Aug 12, 2002 at 23:09 UTC
    You haven't allowed for overlapping regions. If I want to print out any line matching "foo" plus the next 3 lines in the following
    qux foo bar 1 bar 2 foo bar 1 bar 2 bar 3 qux

    Then I'd want all the lines printed from the first "foo" to the last "bar", yours only prints up to the second "foo" line.

      You haven't allowed for overlapping regions...

      I did consider it, and decided I didn't want/need that behavior, though it would make for another nifty command line option if that is the behavior you want. Feel free to patch...

      Update: Ok, its updated, though I suppose now you'll want an option to print lines from the next file if the match is at the end of the current file... :-) (though that would be easy, I think replacing the eof line with this would do it):

      last if $opt_f ? eof() : eof;
        You make it sound like that behavior is non-standard in some way, but it is exactly what standard GNU grep does for the -A (--after-context) option and is typically what people would expect if you say your grep will print N lines following each matching line.