Return Code of multiple inner executions

Need some clarification on two topics and I apologize for the long post.

Topic 1). I have a PERL script which sends output to the console. This is executed like a daemon script. I would like to capture the "print" commands of the script to a log file. How can this be done.

Topic 2) Within the same PERL program I have a call to a Shell script whose return status code is captured. However within the Shell script the last line of execution is a call to java program as "java java_pgm config_file arg_1"
Within the java program, an explicit exit status of "1" is returned if there are any issues encountered in the java program execution; else "0" is returned.
The issue is when the java program has encounters exception. In this situation the shell script returned a value of 256 and the java program returned the following messages to the console.
javax.mail.internet.AddressException: Illegal whitespace in address in string ``. .''
at javax.mail.internet.InternetAddress.checkAddress(InternetAddress.java(Compiled Code))
at javax.mail.internet.InternetAddress.parse(InternetAddress.java(Compiled Code))
at javax.mail.internet.InternetAddress.parse(InternetAddress.java:529)
at javax.mail.internet.InternetAddress.parse(InternetAddress.java:506)
at com.xxxxx.utilities.emailnotifier.EmailNotifier.getMailMessage(EmailNotifier.java:274)
at com.xxxxx.utilities.emailnotifier.EmailNotifier.main(EmailNotifier.java:75)
Any ideas as to how to why a value of 256 was returned as opposed to either a "1" or a "0". I did not write the java program. Thanks

This is because the java application is exiting due to an uncaught exception, and never encounters the exit(1); statement in it's code. In short it's baddly written code.

For Q #1 -
redirect the output to a logfile like this

perl myscript.pl > ~/logfile

"~/" is Korn shell for home directory

I was hoping there would be something similar in PERL to perform the redirection within the program instead of actually using the command line redirection (for example we can use exec >> $LOGFILE 2>&1 in shell programming). Is there any such equivalent to redirect STDOUT and STDERR to a logfile within PERL program? Please advise. Thanks.

Jerardfjay

There is a way to do this in PERL. You could open a log file for writing. and since the default file handle for print commands is always the currently selected file handle, you can use the select the file handle for the log file before you use your print commands. This way you are directing all of your print command outputs to the log file instead of the console.

open (LOGFILE, $logfile);
select (LOGFILE);

print 1;
.
.
print 2;
.
.

The only issue concern that I have for this method is that, I don't believe that STDERR will be redirected to the logfile. Note to keep in mind is that "select (Filehandle)" is a stick operation. It works well in this situation, however if you have a need to make interaction with the user from within the program you have to explicitly specify select (STDOUT) to redirect output to console again.

Jerardfjay

There is I/O redirection in Perl. Try this:

#!/usr/bin/perl
{
local *STDOUT;
local *STDERR;

open FILE, ">>log.txt" || die "Cannot write to log file!";
open STDOUT, ">&FILE" || die "Cannot redirect STDOUT";
open STDERR, ">&FILE" || die "Cannot redirect STDOUT";

print STDOUT "A\n";
print STDERR "B\n";
}
print STDOUT "C\n";
print STDERR "D\n";

This will redirect STDOUT and STDERR to FILE. Note that outside the block, the STDOUT and STDERR will be restored, because the filehandles in the block are local.

Thanks cbkihong. I executed your piece of Code and the got the following output.

C
D

and the contents of log.txt provided

B
A

Why, is the order of the log file in reverse eventhough the order of the print commands is A and then B. Also I did a chmod -w on the current directory and tried your snippet. I was either expecting to see "Cannot write to log file" on the console, however the only output that I got was

C
D

As always your feedback is greatly appreciated. Thanks. :confused:

Sorry .. i didn't see the post until today. Hopefully you will still remember this.

As STDOUT and STDERR are duped filehandles of FILE, you can treat them as two separate pipes to the output file despite they end up being connected to the same output file, and the two channels are not necessarily synchronized.

Recall that Perl IO is buffered if you use the print() function. Data written to filehandles using these functions are not written immediately but buffered independently. So although the write is sequential, the resulting sequence may be in any interleaving order.

The only way I can think of to force the ordering is to wrap the logging routine inside an exclusive lock with flock(). When the lock is released, Perl will be forced to flush the filehandle, so the correct order will always be preserved by blocking out-of-order writes:

#!/usr/bin/perl

sub toLog {
 local *HANDLE = shift;
 my $msg = shift;
 flock HANDLE, Fcntl::LOCK_EX;
 print HANDLE $msg;
 flock HANDLE, Fcntl::LOCK_UN;
}

{
 local *STDOUT;
 local *STDERR;
 local *FILE;

 open FILE, ">log.txt" || die "Cannot write to log file!";
 open STDOUT, ">&FILE" || die "Cannot redirect STDOUT";
 open STDERR, ">&FILE" || die "Cannot redirect STDOUT";

 toLog(STDOUT, "A\n");
 toLog(STDERR, "B\n");
}
print STDOUT "C\n";
print STDERR "D\n";

which on my system will now give the correct order instead of 'B', 'A'. I may be wrong in this regard given my limited knowledge in I/O in general and the way it is implemented in Perl. So if you have less clumsy ways to achieve this or would like to make corrections, do it.

Thanks cbkihong. I shall try the options that you have provided. I probably will not understand your explanation until I have actually tried the various forms that you have suggessted. Thanks Jerardfay. :slight_smile:

If the previous method looks bizarre to you, here's another method discussed in the Perl FAQ:

#!/usr/bin/perl

use IO::Handle;

{
 local *STDOUT;
 local *STDERR;
 local *FILE;

 open FILE, ">log.txt" || die "Cannot write to log file!";
 open STDOUT, ">&FILE" || die "Cannot redirect STDOUT";
 open STDERR, ">&FILE" || die "Cannot redirect STDOUT";

 STDOUT->autoflush(1);
 STDERR->autoflush(1);

 print STDOUT "A\n";
 print STDERR "B\n";
}
print STDOUT "C\n";
print STDERR "D\n";

I have problems with this autoflush() on socket I/O so I tend to avoid it most of the time, but in this case it just seems to work quite well.

As always, there are a lot of ways to do the same thing in Perl. :wink: