How to redirect STDERR within script? - perl

I can redirect STDERR from the command line by doing:
perl myscript.pl 2> err.txt
How can I do this within the script so that the person running the script doesn't have to do it?

This is what I do
open STDERR, '>', "$errfile"
or warn "Cannot redirect STDERR to $errfile: $!";
If this is in a library, call carp instead of warn. That requires use Carp;, for core module Carp.
In case the STDERR stream will need to be restored first save it, as shown in open.

Related

Meaning of open( STDERR, ">&STDOUT" )

i find this in one sample script,
then i search from google and found the following words,
Note that you cannot simply open STDERR to be a dup of STDOUT in your Perl
program and avoid calling the shell to do the redirection. This doesn't
work:
open(STDERR, ">&STDOUT");
This fails because the open() makes STDERR go to where STDOUT was going at
the time of the open(). The backticks then make STDOUT go to a string, but
don't change STDERR (which still goes to the old STDOUT).
Now I am confused. What exactly is the meaning of open(STDERR, ">&STDOUT"); ?
With the & in the mode >& in the call
open STDERR, ">&STDOUT"; # or: open STDERR, ">&", \*STDOUT
the first given filehandle is made a copy of the second one. See open, and see man 2 dup2 since this goes via dup2 syscall. The notation follows the shell's I/O redirection.
Since here the first filehandle exists (STDERR)† it is first closed.
The effect is that prints to STDERR will go to where STDOUT was going before this was done, with the side effect of the original STDERR being closed.
This is legit and does not result in errors but is not a good way to redirect STDERR in general -- after that we cannot restore STDERR any more. See open for how to redirect STDERR.
The rest of the comment clearly refers to a situation where backticks (see qx), which redirect STDOUT of the executed command(s) to the program, are used after that open call. All this seems to refer to an idea of redirecting STDERR to STDOUT in this way.
Alas, the STDERR, made by that open call to go where STDOUT was going, doesn't get redirected by the backticks and thus still goes "there." In my case prints to STDERR wind up on the terminal as I see the warning (ls: cannot access...) with
perl -we'open STDERR, ">&STDOUT"; $out = qx(ls no_such)'
(unlike with perl -we'$out = qx(ls no_such 2>&1)'). Explicit prints to STDERR also go to the terminal as STDOUT (add such prints and redirect output to a file to see).
This may be expected since & made a copy of the filehandle, so the "new" one (the former STDERR) still goes where STDOUT was going, that is to the terminal. What is of course unintended in this case and thus an error.
† Every program in UNIX gets connected to standard streams stdin, stdout, and stderr, with file descriptors 0, 1, and 2 respectively. In a Perl program we then get ready filehandles for these, like the STDERR (for fd 2).
Some generally useful posts on manipulations of file descriptors in the shell:
What does “3>&1 1>&2 2>&3” do in a script?
In the shell, what does “ 2>&1 ” mean?
File descriptors & shell scripting
Order of redirections
Shell redirection i/o order
It's basically dup2(fileno(STDOUT), fileno(STDERR)). See your system's dup2 man page.
In short, it associates STDERR with the same stream as STDOUT at the system level. After the command is performed writing to either will be the same as writing to STDOUT before the change.
Unless someone's messed with STDOUT or STDERR, it's equivalent to the shell command
exec 2>&1

Perl STDERR redirect failing

A common functions script that our systems use uses a simple STDERR redirect in order to create user-specific error logs. it goes like this
# re-route standard out to text file
close STDERR;
open STDERR, '>>', 'd:/output/Logs/STDERR_' . &parseUsername($ENV{REMOTE_USER}) . '.txt'
or die "couldn't redirect STDERR: $!";
Now, I copy-pasted this to my own functions script for a system-specific error log, and while it'll compile, it breaks the scripts that require it. Oddly enough, it doesn't even print the error that the children script are throwing. My slightly modified version looks like,
close STDERR;
open (STDERR, '>>', 'err/STDERR_SPORK.txt')
or die print "couldn't redirect STDERR: $!";
everything compiles fine in command prompt, -c returns ok, and if I throw a warn into the function script, and compile, it outputs properly. I still do not understand why though this kills the children. I cut out the redirect, and sure enough they work. Any thoughts?
die (and warn) writes to STDERR. If you close STDERR and then need to die as you attempt to reopen it, where would you expect to see the error message?
Since this is Perl, there are many ways to address this issue. Here are a couple.
open the file first to a tmp filehandle, reassign it to STDERR if everything goes ok
if (open my $tmp_fh, '>>',
'd:/output/Logs/STDERR_' . &parseUsername($ENV{REMOTE_USER}) . '.txt') {
close STDERR;
*STDERR = *$tmp_fh;
} else {
die "couldn't redirect STDERR: $!";
}
Use con. For programs that you run from a command line, most systems have a concept of "the current terminal". In Unix systems, it's /dev/tty and on Windows, it's con. Open an output stream to this terminal pseudo-file.
open STDERR, '>>',
'd:/output/Logs/STDERR_' . &parseUsername($ENV{REMOTE_USER}) . '.txt'
or do {
open my $tty_fh, '>', 'con';
print $tty_fh "couldn't redirect STDERR: $!";
exit 1;
};
After changing nothing in the script, and poking around in the server, and changing nothing, it now works as expected. I don't know what to say to be honest.

BlueHost avoid main error log

My Perl application (in BlueHost hosting) is generating some errors. This error can be shown in the Main Error Log of BlueHost (which is shared):
/var/log/domlogs/error_log
I don't understand how my errors appears in that file, because in my code I am not referencing it anywhere. How does this process works? Is there any way to avoid my errors appear in that file?
You can redirect STDERR yourself without needing an external module:
use strict;
use warnings;
use autodie;
BEGIN {
open my $fh, '>>', 'myerror.log';
close STDERR;
*STDERR = $fh;
}
warn "Hello world";
die "Bye world";
The above script closes the default STDERR and opens my own. It waits to close until after the open command is called just in case there is some error with the file we're trying to log to.
Log file will report:
Hello world at scratch.pl line 11.
Bye world at scratch.pl line 13.
This line solved my problem:
use Tie::STDERR '>> /home/user/public_html/project/log/error_log.log';
I had to install the perl module:
Tie:STDERR
Information taken from:
http://www.adelton.com/perl/Tie-STDERR/

How to keep open STDOUT and STDERR with Daemon::Simple

I've been using Daemon::Simple to quickly Daemonise some scripts and also make them start/stop capable. However, some of these scripts are written in python, so I generally call Daemon::Simple::Init() then exec() the python script.
However, I've found that Daemon::Simple::Init() closes STDOUT and STDERR, and it seems as a result of this the python scripts break (just exit) when they write to STDOUT and STDERR. Reopening STDOUT and STDERR and redirecting them to a file before the exec does not help.
What I've found does help is changing the source of Daemon::Simple from this:
close(STDOUT);
close(STDERR);
to:
open STDOUT, "/dev/null"
open STDERR, "/dev/null"
If I then again open STDOUT and STDERR and redirect them to real files after calling Daemon::Simple:Init() like before, this time it works. It seems that closing STDOUT and STDERR and reopening somehow breaks them post exec(), but opening them to /dev/null and reopening them works fine.
Is there anyway I can reopen or keep open STDOUT and STDERR without modifying Daemon::Simple which survives an exec()?
Are those the same lines you were using to try to reopen STDERR and STDOUT? If so the problem is likely that you're opening them for reading not for writing. Try using
open STDOUT, '>', '/dev/null';
Does this work for you?
use Daemon::Simple;
open(SAVEOUT, ">&STDOUT");
open(SAVEIN, "<&STDIN");
Daemon::Simple::init("daemon");
open(STDOUT, ">&SAVEOUT");
open(STDIN, "<&SAVEIN");
exec("python-script") or die "exec failed: $!";

How can I redirect the output of Perl's system() to a filehandle?

With the open command in Perl, you can use a filehandle. However I have trouble getting back the exit code with the open command in Perl.
With the system command in Perl, I can get back the exit code of the program I'm running. However I want to just redirect the STDOUT to some filehandle (no stderr).
My stdout is going to be a line-by-line output of key-value pairs that I want to insert into a mao in perl. That is why I want to redirect only my stdout from my Java program in perl. Is that possible?
Note: If I get errors, the errors get printed to stderr. One possibility is to check if anything gets printed to stderr so that I can quite the Perl script.
Canonically, if you're trying to get at the text output of a forked process, my understanding is that's what the backticks are for. If you need the exit status as well, you can check it with the $? special variable afterward, e.g.:
open my $fh, '>', "output.txt" or die $!;
print {$fh} `echo "Hello!"`;
print "Return code: $?\n";
Output to STDERR from the command in backticks will not be captured, but will instead be written directly to STDERR in the Perl program it's called from.
You may want to check out IPC::System::Simple -- it gives you many options for executing external commands, capturing its output and return value, and optionally dying if a bad result is returned.
This is one of the ways to do it.
open my $fh, '>', $file;
defined(my $pid = fork) or die "fork: $!";
if (!$pid) {
open STDOUT, '>&', $fh;
exec($command, #args);
}
waitpid $pid, 0;
print $? == 0 ? "ok\n" : "nok\n";
Use open in -| mode. When you close the filehandle, the exit status will be in $?.
open my $fh, '-|', "$command"; # older version: open my $fh, "$command |";
my #command_output = <$fh>;
close $fh;
my $command_status = $?;
From perldoc -f close
If the file handle came from a piped open, "close" will
additionally return false if one of the other system calls
involved fails, or if the program exits with non-zero status.
(If the only problem was that the program exited non-zero, $!
will be set to 0.) Closing a pipe also waits for the process
executing on the pipe to complete, in case you want to look at
the output of the pipe afterwards, and implicitly puts the exit
status value of that command into $? and
"${^CHILD_ERROR_NATIVE}".