tags:

views:

618

answers:

6

My code won't run the last line right before " exit; " and I have no clue why. I tried to put an additional printf $fh line before exit, but that didn't work either; it would not print either line. Everything else prints fine except the last print statements before the exit.

Any clue why this happens? Or better yet, how to fix or work around it?

    if($i>0 && $i<3)
    {
        printf $fh "Partial match found.\n";
        if($matched_uid){printf $fh "UID #: ".$arguid." exists.\n";}
        if($matched_id){printf $fh "ID #: ".$argid." exists.\n";}
        if($matched_uname){printf $fh "Username: ".$arguname." exists.\n";}
        printf $fh "Aborting."; 
        exit;
    }

EDIT:
Part of the code I copied contained this

select $fh; $| = 1; #set auto flush

Maybe this is why my results couldn't be duplicated....

+8  A: 

Try "Aborting.\n"

This looks like a buffering issue.

David Norman
Although this works in this case, in some cases due to specifications, an extra character at the end of the file may be non-permissible. So do consider the other answers.
Kent Fredric
You're right, it does look like a buffering issue, but that is very surprising considering the program is exiting normally and I would have sworn Perl (through stdio) flushes buffers on normal exit.
Adrian Pronk
I'm surprised, as well.
David Norman
I'm trying to replicate the problem and being quite unsuccessful. Something foul seems at play here. Anyone have ideas?
Kent Fredric
I've extended my answer with probing deeper. Would like you're thoughts on the situation.
Kent Fredric
i could just be using an older version of perl... don't know how to find out the version
CheeseConQueso
perl -V aught to tell you. I'm using 5.10 so that could be why I can't replicate
Kent Fredric
+2  A: 

You need to add a trailing \n to the last printf. Otherwise the output doesn't get flushed to the console.

rjh
+8  A: 

Perl buffers its output. You can turn off autobuffering with

local $| = 1;

Edit: I somehow hit ! instead of | the first time... fixed. No idea how that happened, they're on opposite sides of the keyboard.

R. Bemrose
A: 

Either David Norman is right, or the problem is somewhere else. I just typed the code inside the if block into the perl interpreter, and it printed "Aborting." as expected. (It worked for me even without the terminating newline character.)

David Hanak
maybe its because im going through some other interface before i get to the perl compiler... dont know
CheeseConQueso
+10  A: 

I gave R. Bemrose my vote, but if $fh is a FILE HANDLE you may want to also close it prior to terminating.

Also, you are using printf wrongly. printf is not 'print file'. its 'print format'.

For that last example

  print $fh 'Message' ;

Is all you need.

printf is intended to be used thus:

printf $fh 'some format %s  and more %s ' ,  $var , $var ;

or

   printf 'some format %s  and more %s ' ,  $var , $var ;

and its equivelant to doing

   print $fh sprintf 'format %s more %s' , $var , $var ;

And because of this, you're putting needless code risks in in the event any of your concatenated variables expand to have '%' in them. For an example of people falling into this trap, see Q:308417:Why does my Perl script remove characters from the file?

perldoc -f print

print FILEHANDLE LIST
print LIST
print   Prints a string or a list of strings.

perldoc -f printf

printf FILEHANDLE FORMAT, LIST
printf FORMAT, LIST
      Equivalent to "print FILEHANDLE sprintf(FORMAT, LIST)", except that "$\" 
      (the output record separator) is not appended

Further Probing

I've been probing into your problem further because I can't get your "buffering" condition to occur. All my files are properly flushed whether or not I flush them. ( Perl 5.10 ).

Heres my code snippet I'm trying:

#!/usr/bin/perl 

use strict;
use warnings;


open my $fh , '>' , '/tmp/oo.txt'; 

my $exit_at = int(rand( 200_000 ));
print "$exit_at\n";
sleep 2;
for ( 0 .. ( $exit_at * 2 ) ){ 
    print $fh $_;
    if ( $_ == $exit_at ){
        printf $fh '%n' ; # Bad Code Causes Early Death. 
        printf $fh 'last line';
        exit; 
    }
    if ( $_ % 1000  ){
        print $fh "\n";
    }
}

I'm using your miss-use of printf. The line that is commented ejects an error message, because its not valid due to nobody providing the 'n' parameter, and it dies with:

Modification of a read-only value attempted at iotest.pl line 15.

Before executing 'last line' print ( also bad, but theres no % symbols in it so its fine ) making 'last line' absent from the output file.

Please fix up your printf and use plain-old-print instead and see if that solves your problem.

Kent Fredric
Perl should close $fh automatically a) when it goes out of scope, and b) when the program exits. However, I agree that it's a Good Thing to explicitly close them when you're done. Plus, it flushes the buffer without needing to append a "\n" OR fiddling with builtin variables, which can get ugly.
Chris Lutz
Perhaps he has an old version of perl?
David Norman
I stole the printf sytnax from another perl script... thanks for clearing that up though. i changed them all back to regular print and it works the same as intended. as far as the error goes, i dont know why you guys can't replicate it..
CheeseConQueso
+3  A: 

On the subject of proper use of print, you make things a little hard on yourself.

Interpolate your variables instead of concatenating the strings together. Use one print statement with a list of strings to print, instead of multiple strings.

Here's one way:

if( $i>0 && $i<3 )
{
    print $fh join "\n", 
        "Partial match found.",
        $matched_uid   ? "UID #: $arguid exists."      : (),
        $matched_id    ? "ID #: $argid exists."        : (),
        $matched_uname ? "Username: $arguname exists." : (),
        "Aborting.",
        ;

    exit;
}

The () on the false side of the ternary operator is an empty list, which will disappear when the arguments to join are flattened. If I had used and empty string (''), each non-matching criterion would have produced an extra blank line in the results.

You could also add your own newline on each item, and then skip the join, if you feel this is more readable:

    print $fh  
        "Partial match found.\n",
        $matched_uid   ? "UID #: $arguid exists.\n" : '',
        ...
daotoad