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

Hi Monks:

In code block below, the saved fh's are not restored and a print command executed later in a different module prints to my log. If I close the handle it produces a print to closed handle error.

The email is completed and sent and the logging shows continued posting to the $fh/log after returning to the caller and the callers caller.

How do I restore the handles and close $fh?

Looks to me like some examples I found in "The Monastery"

Thanks

sub SendMemberMail { my ($subject,$from,$to,$port,$cc,$bcc,$heading,$body,$closing,$signi +ture,$auth) = @_; # Open log for recording during execution of SendMail my $sendMail_logfile = '/home/abcus/public_html/httpsdocs/cgi-bin/li +b/perl/Mail/mail.log'; open(my $fh, "+<", "$sendMail_logfile") or die "Opening: $!"; my $previous_STDOUT_fh = select(STDOUT); my $previous_STDERR_fh = select(STDERR); #Redirect STDERR & STDOUT to $fh for this sub *STDOUT = $fh; *STDERR = $fh; my $message = ($heading . "\n" . "\n" . $body . "\n" . "\n" . $closing . "\n" . "\n" . $signiture . "\n" ); if ($auth == 1) { $auth = {user=>'webmaster@abc.us', password=>'wm@xxxx', method=>'P +LAIN LOGIN', REQUIRED=>1} } elsif ($auth == 0) { $auth = ""; } my %mail = ( To => $to, From => $from, Cc => $cc, Bcc => $bcc, Auth => $auth, Port => $port, Subject => $subject, Message => $message, debug => 18 ); SendMail(%mail) or die $Mail::sendMail::error; $log .= "Sending eMail Succeded\n"; #Print the data from the $log. print $fh $log; #restore the standard handles select($previous_STDOUT_fh); select($previous_STDERR_fh); close($fh); return 1; }

Replies are listed 'Best First'.
Re: STDERR Restore after redirect
by choroba (Cardinal) on Apr 26, 2018 at 22:12 UTC
    Store stdout and stderr to variables, restore them later. No select is needed.
    #!/usr/bin/perl use warnings; use strict; open my $fh, '>', '1.log' or die $!; my $stdout = *STDOUT; my $stderr = *STDERR; *STDOUT = $fh; *STDERR = $fh; print "stdout 1\n"; warn "stderr 1\n"; *STDOUT = $stdout; *STDERR = $stderr; print "stdout 2\n"; warn "stderr 2\n";
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

      Appreciate response.

      However, problem persists.

      The filehandle you're printing on got itself closed sometime before now. Check your control flow. print() on closed filehandle

      This makes no sense to me

        I blocked the call to sendmail and the handles were restored and the print command elsewhere executed.

        # SendMail(%mail) or die $Mail::sendMail::error;

        unblocking and

        #restore the file standard handles *STDOUT = $stdout; *STDERR = $stderr; close($fh); print "stdout 2\n";

        results in: print() on closed filehandle $fh at /home/abcus/public_html/httpsdocs/cgi-bin/lib/perl/manageusers.pm line 1124. stderr 2

        So sendMail call is causing problem

        ugh

Re: STDERR Restore after redirect
by eyepopslikeamosquito (Archbishop) on Apr 28, 2018 at 05:23 UTC
Re: STDERR Restore after redirect
by tybalt89 (Monsignor) on Apr 27, 2018 at 00:42 UTC

    Why save and restore when local() will do that for you.

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1213644 use strict; use warnings; warn "before on STDERR\n"; test(); warn "after on STDERR\n"; sub test { my $log = ''; local *STDERR; open STDERR, '>', \$log or die; warn "test message should be in log\n"; close STDERR or die; print "log contents: $log"; }

    Outputs:

    before on STDERR log contents: test message should be in log after on STDERR

      Thanks

      Had already tried that with same result

      Mental block on scoping

      Have block of code that calls a sub which calls another sub which returns result(0 or 1) after allegedly closing the handle opened in that last sub. The result propagates back to the original block which then tries to print a response back to the client. That print command is what comes up against the closed handle. The close was after STDERR AND STDOUT were restored so I can't understand how the STDOUT was closed.

      If the handles are closed, perhaps reopening them.

        Anything anywhere in the SendMail package could have done it. I think you need to take a different approach to this.
Re: STDERR Restore after redirect
by djerius (Beadle) on Apr 27, 2018 at 17:19 UTC
    I've written a module which attempts to make filehandle saving and restoring less problematic: IO::ReStoreFH. Perhaps it would help in your situation.

      Hi. I will take a look

      As it now stands, code below works except for one quirk.

      sub SendMemberMail { my ($subject,$from,$to,$port,$cc,$bcc,$heading,$body,$closing,$signi +ture,$auth) = @_; warn("In SendMemberMail before redirect of STDERR & STDOUT"); # Open log for recording during execution of SendMail my $sendMail_logfile = '/home/abcus/public_html/httpsdocs/cgi-bin/li +b/perl/Mail/mail.log'; open (my $fh, "+<", "$sendMail_logfile") or die "Opening: $!"; #Redirect STDERR & STDOUT to $fh for this sub open my $oldSTDOUT, ">&STDOUT"; open OLDERR, ">&",\*STDERR; open (STDERR, ">>&=", $fh) || die "can't redirect STDERR"; open (STDOUT, ">>&=", $fh) || die "can't redirect STDOUT"; $fh->autoflush(1); my $message = ($heading . "\n" . "\n" . $body . "\n" . "\n" . $closing . "\n" . "\n" . $signiture . "\n" ); if ($auth == 1) { $auth = {user=>'webmaster@abc.us', password=>'xx@33333XX', method= +>'PLAIN LOGIN', REQUIRED=>1} } elsif ($auth == 0) { $auth = ""; } my %mail = ( To => $to, From => $from, Cc => $cc, Bcc => $bcc, Auth => $auth, Port => $port, Subject => $subject, Message => $message, debug => 0 ); SendMail(%mail) or die $Mail::sendMail::error; $log .= "Sending eMail Succeded\n"; #Print the data from the $log. print $fh $log; #Test redirected handles print STDOUT "stdout 1\n"; warn("warn stderr 1\n"); close($fh); #restore the file standard handles open(STDOUT, ">&", $oldSTDOUT) or die "Can't dup \$oldout: $!"; open(STDERR, ">&OLDERR") or die "Can't dup OLDERR: $!"; #Test original handles print STDOUT "stdout 2\n"; warn("warn stderr 2\n"); return 1; }

      The mail is sent. The handles are restored as evidenced by stderr2 in my regular error log file and stderr/stdout in my pre-restore log file. What is gone in the nether world is the stdout2 and the printing of the HTML form to stdout back to client.

      If I don't close the handle, the form (text) is printed to the sendMail logfile.

      I put warns at beginning of the sub to print the html and at the end and both appear in the error log indicating execution of that block but no form returned to client.

      As I posted earlier, if I bypass the call to sendMail(), the form prints as expected.

      For some reason the call to print the form is not directed to the (opened)STDOUT nor to the (closed) $fh log handle. Perhaps lost in the mist of time and sitting at the earth moon L4 LaGrange point in a huge cloud of missing documents.

      I can't see anything in sendMail that could have such an effect. I shut off the debugging in sendMail which writes to stderr and no effect (except less output to the log naturally)

      I guess I have to just grunt my way through this until a revelation hits me.

      Scoping are weird!

        Just use local() within a block as tybalt89 suggested to save and then restore the filehandles.

        sub SendMemberMail { my ($subject,$from,$to,$port,$cc,$bcc,$heading,$body,$closing,$signi +ture,$auth) = @_; warn("In SendMemberMail before redirect of STDERR & STDOUT"); $Mail::sendMail::mailcfg{'debug'} = 6; $Mail::sendMail::mailcfg{'port'} = $port; my $message = "$heading\n\n$body\n\n$closing\n\n$signiture\n"; my %mail = ( To => $to, From => $from, Cc => $cc, Bcc => $bcc, Subject => $subject, Message => $message, ); if ($auth == 1) { $mail{'Auth'} = { user => '', password => '', method => 'PLAIN LOGIN', required => 1 }; }; my $logfile = '/home/abcus/public_html/httpsdocs/cgi-bin/lib/perl/Ma +il/mail.log'; open my $fh, '>>', $logfile or die "Error opening $logfile : $!"; SEND: { local *STDOUT = $fh; local *STDERR = $fh; if ( SendMail(%mail) ) { print $Mail::sendMail::log; } else { print $Mail::sendMail::error; } } close $fh; select STDOUT; # Test original handles print "stdout 2\n"; warn("warn stderr 2\n"); return 1; }
        Note: Using Mail::sendMail as described here
        poj
Re: STDERR Restore after redirect
by Anonymous Monk on Apr 30, 2018 at 02:07 UTC