I was recently challenged to code a program in as few lines as possible by a friend. The challenge, to read a file and look for a pattern in a line. If it found it, then the program would print that full line and a number of lines after it.

After some thought, I came up with the script below:
open(T,"<$ARGV[0]"); my (@data,$count); foreach((@data = <T>)){++$count;(/$ARGV[1]/)?eval{print;for(1..$ARGV[2 +]){print $data[$count] if defined $data[++$count];};exit()} : undef;} close(T);

The data file contained:
one two thrtestee four five six werwr werwer seen?

And the program was invoked as
# perl -w script.pl datafile pattern numlines perl -w script.pl data.dat test 4

I wondered if this could be improved upon at all or if there were any unusual tricks I could have used. Had fun anyway :)

Replies are listed 'Best First'.
Re: Reading a pattern and a number of lines after it
by blakem (Monsignor) on Jan 09, 2002 at 04:45 UTC
    With a pattern of /ou/ and numlines set to 3 I get:
    perl -ne '$x=/ou/..0; print if $x && $x-1 <= 3' data.dat
    As in:
    % cat data.dat one two thrtestee four five six werwr werwer seen? % perl -ne '$x=/ou/..0; print if $x && $x-1 <= 3' data.dat four five six werwr
    Notice how it prints the matching line plus an additional $numlines lines...

    -Blake

      thanks :)

      Looks like I have still got quite a few tricks to learn!
Re: Reading a pattern and a number of lines after it
by jryan (Vicar) on Jan 09, 2002 at 05:39 UTC

    Shenanagians! I call Shenanagians! blakem broke the rules ;)

    The original stipulations stated that the arguments would be supplied via command line; here is the shortest I came up with with that:

    open(T,"$ARGV[0]"); foreach(@_=<T>){/$ARGV[1]/?print@_[$c..$ARGV[2+$c],exit:$c++} close(T)

    Unfortunately, much space is taken up by the $ARGV. Also, note that by changing the exit to a "\n" (or removing it completely) will cause the code will print $ARGV[2] lines for ALL matches. Leaving the exit causes only the first batch to print.

    Update:
    I can cheat too (using blakem's arguments as an example):

    perl -ne "print((<>)[0..3])if/ou/" test.txt
    and completely floor him; but, as I said, that would be cheating ;)

      You didn't print the line that the match was on, which was stipulated. I also don't like having it wait forever for more input files. A better cheating solution is therefore:
      perl -ne'die$_,(<>)[0..3]if/ou/' test.txt
      (Erm, OK. I may have just printed to the wrong filehandle... :-)
        I stand corrected; your cheating solution through the use of additional cheating is much better than mine! I just whipped mine up real quick to spite blakem, I didn't check it as thoroughly as I should :)
Re: Reading a pattern and a number of lines after it
by Juerd (Abbot) on Jan 09, 2002 at 07:03 UTC
    Using $0 file pattern number, just like the original
    ($r,$i,$l,@ARGV)=@ARGV[1,2,2,0];(/$r/and$i='.'or++$i<=$l)&&print while +<>

    2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$