Dru has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

This is probably an easy one, but it's escaping me at the moment.

I have a snippet of code below that I want to print out 500 lines of each ip, but I just keep getting 500 lines of the first ip. I know I'm probably missing a loop control or something, but I just can't figure it out.

I would greatly appreciate any assistance.

Thanks,
Dru
open (LOG, "$fw_logfile") or die and mail_error($!); open (OUTFILE, "+>>$outfile") or die and mail_error($!); my $seen = 0; while(<LOG>){ foreach my $i (@newips){ unless($seen == 500){ print OUTFILE if (/\;drop;[^"]*$i/) && $seen++; } } }

Replies are listed 'Best First'.
Re: Correct Loop Structure
by VSarkiss (Monsignor) on Jan 29, 2004 at 17:54 UTC

    It's not the loop structure, it's the variable. You're setting $i to every element of @newips, but printing $_. Either change the foreach to use $_, like so:

    foreach (@newips) {
    Or change the print:
    print OUTFILE $i if ...

    HTH

Re: Correct Loop Structure
by arden (Curate) on Jan 29, 2004 at 18:03 UTC
    You're not resetting the value of $i. Try this:

    open (LOG, "$fw_logfile") or die and mail_error($!); open (OUTFILE, "+>>$outfile") or die and mail_error($!); my $seen = 0; while(<LOG>){ foreach my $i (@newips){ unless($seen == 500){ print OUTFILE if (/\;drop;[^"]*$i/) && $seen++; } } $seen = 0; }
Re: Correct Loop Structure
by ysth (Canon) on Jan 29, 2004 at 19:38 UTC
    Interesting interpretations of your question so far. Here's mine:
    my %seen; while(<LOG>){ foreach my $i (@newips){ print OUTFILE if /\;drop;[^"]*$i/ && ++$seen{$i} <= 500; } }
      ysth,

      Yeah that's what I was thinking ;-)

      Your's and arden's code (from his second response) where the only two that I could get to work. I like the simplicity of your's, but arden's seems to run faster.

      I guess this was a bit more tricky then I though. Thank god for perlmonks. I appreciate the help. -Dru
        Your's and arden's code (from his second response) where the only two that I could get to work. I like the simplicity of your's, but arden's seems to run faster.

        Maybe faster. But arden's code, like yours and all the others except ysth's, have a major flaw: they will skip the first (matching) field of your log file!

        If you decide to use arden's solution, change $newIPseen{$i}++ to ++$newIPseen{$i}

        dave

        If the performance isn't good enough, you can probably increase it a fair amount by doing something like this (untested):
        my %seen; my %newip_res; for my $ip (@newips) { $seen{$ip} = 0; $newip_res{$ip} = qr/;drop;[^"]*$ip/; } while(<LOG>){ foreach my $ip (@newip) { ++$seen{$ip}, print OUTFILE if $seen{$ip} != 500 && /$newip_res{$i +p}/; } }
        If there are more than a very few lines that don't have ";drop;" in them, also put a  next unless /;drop;/; just before the foreach.
Re: Correct Loop Structure
by bmann (Priest) on Jan 29, 2004 at 18:01 UTC
    Once $seen hits 500, it never gets incremented again.

    Put $seen = 0; inside the while loop but outside of the foreach.

    Update: Although I'd probably do something clsoer to

    while(<LOG>){ foreach my $i (@newips){ if (/\;drop;[^"]*$i/) { print OUTFILE for 1 .. 500; } } }

    (Untested)

Re: Correct Loop Structure
by arden (Curate) on Jan 29, 2004 at 19:18 UTC
    After further consideration, if you're wanting to print up to 500 lines total for each unique IP#, you should try a hash instead of the array. Take a look at this:

    open (LOG, "$fw_logfile") or die and mail_error($!); open (OUTFILE, "+>>$outfile") or die and mail_error($!); my %newIPseen; foreach my $i ( @newips ) { $newIPseen{$i} = 0; } while (<LOG>) { foreach $i ( keys( %newIPseen ) ){ unless ( $newIPseen{$i} == 500 ) { print OUTFILE if (/\;drop;[^"]*$i/) && $newIPseen{$i}++; } } }

    Now, if you load the hash %newIPseen in the first place instead of the array @newips, you can get rid of the first foreach loop.

Re: Correct Loop Structure
by Art_XIV (Hermit) on Jan 29, 2004 at 18:47 UTC

    TMTOWTDI, this way with a slice:

    open (LOG, "$fw_logfile") or die and mail_error($!); open (OUTFILE, "+>>$outfile") or die and mail_error($!); my $seen = 0; while(<LOG>){ foreach my $i (@newips[0..499]){ print OUTFILE if (/\;drop;[^"]*$i/) && $seen++; } }
    Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"