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

The script is supposed to open a file, print the current date to it, close the file, open another file parse out data that I want from access.log and then loop to the next iteration. What I'm seeing in the files that it creates is the data first and the date on the bottom. I'm just beginning with perl and I am used to writing shell scripts. I can't figure out why the data gets printed before the date. Anyone?

#!/usr/bin/perl -w use diagnostics; #This file parses out squids access log for address bar entries to see + where users purposefully go $date=`date`; $net="192.168.4."; $counter=10; $logpath="/var/log/squid/userarchive/"; $ip=$net.$counter; #The counter is set to the max ipadress +1 while ( $counter <= 14 ) { #Print the date to each file we're writing to open(LOGF,">>",$logpath . $ip); print LOGF "$date\n"; $|++; close LOGF; #Grab the access log and go through it, splitting each line into eleme +nts of the array open(INFILE,"/var/log/squid/access.log") or die "Can’t open /v +ar/log/squid/access.log: $!"; while ( <INFILE> ) { @array = split(" ",); $ip=$net.$counter; #Only grab things associated with the current searched for ip and ends + with .com .net and so on if (($array[7] =~ /[com|org|net|gov|cc|mil]\/$ +/)&&($array[3] eq $ip)) { #Write it to the log file open(LOGF,">>",$logpath . $ip) or die +"Can't open $logpath$ip: $!"; print LOGF "$ip \: $array[0] +$array[7]\n"; close LOGF; } } $counter++; }

Replies are listed 'Best First'.
Re: Perl printing in the wrong order
by Eliya (Vicar) on Apr 29, 2011 at 12:47 UTC

    You're changing the file name ($ip) in the wrong place, so the first file gets two dates, the last one none, and the remaining get the date (which belongs to the next file) at the bottom...

    I.e.,

    you have: you want: ------------------------ ------------------------ date date (first file) (first file) ...data... ...data... date ------------------------ ------------------------ date ...data... ...data... date ------------------------ ------------------------ date ...data... ...data... date ------------------------ ------------------------ date ...data... ...data... date ------------------------ ------------------------ date (last file) (last file) ...data... ...data... ------------------------ ------------------------

    Move the

    $ip=$net.$counter;

    to the beginning of the loop:

    while ( $counter <= 14 ) { $ip=$net.$counter; ...

    in which case you also no longer need the first occurrence before the loop.

    (BTW, there's no need to open/close the same file multiple times.)

      Thanks for the reply. That fixed the issue. So your saying that during the first iteration, it wrote to the initialized value, and on the second iteration, the value wasn't changed until after the print , so it put the print in twice on the same file. Ugh! Why do things look so much simpler after they've been solved? Thanks for your help!!

      I reopened the same file because Perl was saying I was trying to print to a closed filehandle. I was thinking that when I opened INFILE that it may have made LOGF close, so I reopened it.

Re: Perl printing in the wrong order
by JavaFan (Canon) on Apr 29, 2011 at 12:57 UTC
     $|++;
    Let's not, shall we? It's a shame this cargo cult still creeps up it's ugly head after 15 years or so - the person making it popular should be ashamed of himself (Yes, I know who you are).

    $|++; has nothing over $|=1;. Not even in the number of keystrokes. Just use $|=1;.

    Not that your use here is useful. You're autoflushing the wrong handle (STDOUT), doing it after the print, closing the handle flushes output as well, and using >> causes writes to happen at the end anyway.

Re: Perl printing in the wrong order
by jwkrahn (Abbot) on Apr 29, 2011 at 13:20 UTC
    $net="192.168.4."; $counter=10; ... $ip=$net.$counter; ... while ( $counter <= 14 ) { ... open(LOGF,">>",$logpath . $ip); ... while ( <INFILE> ) { ... $ip=$net.$counter; ... } } $counter++; }

    That would probably be better as:

    my $net = "192.168.4."; ... for my $counter ( 10 .. 14 ) { my $ip = $net . $counter; ... open(LOGF,">>",$logpath . $ip); ... while ( <INFILE> ) { ... } } }


    $|++;

    At this point in the program the currently selected filehandle is STDOUT so that is the filehandle that will be affected by the change in $|.    In other words, LOGF will not be affected.



    @array = split(" ",);

    That is the same as saying:

    @array = split;


    #Only grab things associated with the current searched for ip and ends + with .com .net and so on if (($array[7] =~ /[com|org|net|gov|cc|mil]\/$ +/)&&($array[3] eq $ip)) {

    In your regular expression the character class [com|org|net|gov|cc|mil] will match one character only and is exactly the same as using [cegilmnortv|], and you have nothing matching the period character like in .com or .net.

Re: Perl printing in the wrong order
by Gulliver (Monk) on Apr 29, 2011 at 13:21 UTC

    To help troubleshoot next time try putting some print statements in to announce each step with couter values, filenames being opened, etc. Inside loops I like to put in things like

    my $file=$logpath . $ip; print "\$counter=$counter: opening $file\n" if $debug; print "\$counter=$counter: printing $date to $file\n" if $debug;
Re: Perl printing in the wrong order
by Anonymous Monk on Apr 29, 2011 at 14:36 UTC
    perl -ne "chomp; my ($ip) = (split q| |,$_)[2]; my $num; if ($ip=~m/\ .(\d+)$/){$num=$1} print qq|$ip\t$num| . `date ` if($num >= 1 && $num +<= 2);" fakesquidlog.txt Prints ======== 192.168.1.1 1Fri Apr 29 09:34:49 Central Daylight Time 2011 192.168.1.2 2Fri Apr 29 09:34:49 Central Daylight Time 2011 (Date format explain: I'm on Windows)
    Fake squid log contains: 134135 22 192.168.1.1 XX 200 HTTP foobar.com johnny huh? JUNK 134136 24 192.168.1.2 XX 200 HTTP foobar.com johnny huh? JUNK2 134137 25 192.168.1.3 XX 200 HTTP foobar.com johnny huh? JUNK3

    You can easily change the line to append to a file using the $ip with  >>.

    There is little sense in writing a large program for something that should be done as a one-liner.

      Wow. Um, yeah. I've been programming Perl for 3 days now. I'm not sure that I'm quite there yet. Thank you though. :)