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

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

Hey all, I'm trying to port a script to win32 from a unix box. I'm running into trouble with a "bad file descrtiptor" when trying to close a file handle. Here is my code:
sub parse_mail { my $mail = $_[0]; print $mail; open (MAIL,$mail) || die "Cannot open $mail, $!"; @mailf = <MAIL>; chomp (@mailf); my $line; foreach $line (@mailf) { print $line; } print @mailf; close (MAIL) || die "Cannot close $mail, $!"; return @mailf; }
When the script gets to the  close (MAIL) I get an error stating
Cannot close C:/windows/desktop/tmp/test.f2mail, Bad file descriptor
The file stated in the error is correct and is what I'm looking for. I'm using forward slashes in the pathname... I've also used single quotes and "\\" to try and get the script to work, but no luck. The print $mail statement, I put in to watch the variable, displays the correct file and path name. I'm using ActiveState and Perl Builder. I would greatly appreciate any help anybody can offer. Thanks in advance for any help anyone can provide. Bradley
Where ever there is confusion to be had... I'll be there.

Replies are listed 'Best First'.
Re: Win32 bad file descriptor
by particle (Vicar) on Jul 05, 2001 at 20:17 UTC
    i've tested this on ActiveState build 516 and 623, on Win2000 and NT4 in my test lab. i added 'use strict' and '-w'. i had to add a my declaration for @mailf, but otherwise, everything works fine--no errors. i see you're running on win98 or ME, but i can't imagine why this is happening.

    ~Particle

Re: Win32 bad file descriptor
by the_slycer (Chaplain) on Jul 05, 2001 at 21:05 UTC
    I've played and played with this on 2k.
    There are 2 ways that I can recreate this error, but neither of them seem applicable.

    First, if I don't have the die statement after the open, and use a bad filename I get the error after the close. This is obvious.

    Second, if the file has only read permissions, and I print to it (with a die statement on the print) I get the same error, which is only slightly less obvious.

    In your code though, you are not modifying the file in anyway, and have the die statement after the open.

    If you attempt to simply open and close the filehandle (for the file that you are having the problem with) in a seperate script does the same thing happen? Is this happening with every file, or is it just one (I ask that because I see this in a subroutine, I'm assuming multiple files)?

    Is this code cut and pasted from your script or did you retype it here? I've retyped the code a couple of times, rather than pasting it in and noticed that my code I'm submitting here and the code I'm having the problem with has occasionaly differed subtly (with the difference being what was causing the problem).
      I did some playing after I submitted my first question and came up with this:
      sub parse_mail { my $mail = $_[0]; # print $mail; open "MAIL","<$mail" || die "Cannot open $mail, $!"; @mailf = <MAIL>; chomp (@mailf); my $line; # foreach $line (@mailf) { # print "$line\n"; # } print @mailf; close MAIL || die "Cannot close $mail, $!"; # return @mailf; }
      The only differences here being: Removed the parenthasis around the filehandle on the open and close and added double quotes around the file handle for the open statement. This was the last hurdle in getting the entire script to work and does so now, thanks to everyones help. I find it odd though. I have dug through the docs that came with the activestate distro and did not come accross anything regarding this matter... I did find a couple of closely related topics, but none that addressed this particular problem I was having. I'm sure that my inexperience is to blame. Here is the entire script just in case you might be interested. Maybe it might shed the right light on why I was having this problem.
      #!/usr/gnu/bin/perl -w # # ###################################################################### +########## use strict; no strict "refs"; use Mail::Sender; my $f2dir = "C:\\windows\\desktop\\tmp\\"; #print "$f2dir\n"; my @names = <$f2dir*.f2mail>; my @mailf; my $file; foreach $file (@names) { if ( (! -d $file) && ($file =~ /\b\.f2mail/) ) { parse_mail($file); }else{ print "Error with $file, $!"; } sender_mail(@mailf); file_clean($mailf[3],$mailf[4],$file); } ###################################################################### +######### # Begin Subroutines # # parse_mail parses the parameter file (*.f2mail) and submits the resu +lts to # Mail::Sender for smtp ###################################################################### +######### sub parse_mail { my $mail = $_[0]; # print $mail; open "MAIL","<$mail" || die "Cannot open $mail, $!"; @mailf = <MAIL>; chomp (@mailf); my $line; # foreach $line (@mailf) { # print "$line\n"; # } print @mailf; close MAIL || die "Cannot close $mail, $!"; # return @mailf; } ###################################################################### +######### # # sender_mail calls the Mail:Sender module and prepares for smtp ###################################################################### +######### sub sender_mail { my @mailf = @_; print $mailf[3]; open (BODY,$mailf[3]) || die "Cannot open $mailf[3], $!"; my @body = <BODY>; close (BODY) || die "Cannot close $mailf[3], $!"; my $sender; ref ($sender = new Mail::Sender ({ from => $mailf[2], smtp => 'humap.segrest.com'})) || die "Send +er error: $sender, $Mail::Sender::Error!\n"; ref (($sender->MailFile( {to => $mailf[0], subject => $mailf[1], msg => "@body", file = +> $mailf[4]}))) || die "Sender error, $sender, $Mail::Sender::Error!, + $!"; return 1; } ###################################################################### +######### # # file_clean unlinks the files specified in the parameter file (*.f2ma +il) and # the parameter file itself ###################################################################### +######### sub file_clean { my $file; foreach $file (@_) { unlink $file || die "Unlink failure: $!"; } return 1; }
      The many commented out print statement I used for trouble shooting. Comments on the code??? Thanks for everyone's help on this. Bradley
      Where ever there is confusion to be had... I'll be there.
        Comment out the line no strict 'refs' and see your open go down in flames. The die at the open will never have an effect because of operator precedence. Feed it a falsified filename an see for yourself. The die at the close has the same problem.

        Essentially your program seems to work because you have turned off the error reporting.
Re: Win32 bad file descriptor
by OzzyOsbourne (Chaplain) on Jul 05, 2001 at 19:35 UTC

    I never use parenthesis on my close statements. I would use close MAIL. I don't know if that helps, but it's all I really noticed.

    -OzzyOsbourne

      I don't think that's the problem. Using parenthesis is perfectly OK. You have to watch out when you leave them out:

      close MAIL || die "couldn't close: $!"; # oops! close MAIL or die "couldn't close: $!"; # correct close(MAIL) || die "couldn't close: $!"; # correct (original post) close(MAIL) or die "couldn't close: $!"; # correct as well
      The point here is operator precedence. In the wrong example the || binds tighter than 'close'. That's why the low precedence logical operators (and, or, not, xor) were introduced.

      -- Hofmator

      I tried that as well. At first I didn't have the parenthesis and added them, but no luck. Bradley
      Where ever there is confusion to be had... I'll be there.