Linux/Perl: Additional output buffers other than STDOUT and STDERR?
Absolutely. The open
command with the >&=
mode allows you to open filehandles on arbitrary file descriptors.
# perl 4fd.pl > file1 2> file2 3> file3 4> file4 5< file5
open NONSTDFOO, '>&=3';
open NONSTDBAR, '>&=4';
open NONSTDBAZ, '<&=5'; # works for input handles, too
print STDOUT "hello\n";
print STDERR "world\n";
print NONSTDFOO "42\n";
print NONSTDBAR <NONSTDBAZ>;
$ echo pppbbbttt > file5
$ perl 4fd.pl >file1 2>file2 3>file3 4>file4 5<file5
$ cat file1
hello
$ cat file3
42
$ cat file4 file2
pppbbbttt
world
PERL Unable to redirect stdout and stderr to output file
Are you Suffering from Buffering?
If so, the data will eventually appear in the log file (unless the program is killed), but you can make it appear there sooner by adding the following to the top of your Perl code:
$| = 1;
The special variable $|
(also called $OUTPUT_AUTOFLUSH
) will force output to be flushed with every write if its value is non-zero. Follow the link to see how to set autoflush per filehandle.
Why output on perl eval differ between common bash output and redirection STDOUT + STDERR into file?
A simpler demonstration of the problem:
$ perl -e'print("abc\n"); warn("def\n");'
abc
def
$ perl -e'print("abc\n"); warn("def\n");' 2>&1 | cat
def
abc
This is due to differences in how STDOUT and STDERR are buffered.
- STDERR isn't buffered.
- STDOUT flushes its buffer when a newline is encountered if STDOUT is connected to a terminal.
- STDOUT flushes its buffer when it's full otherwise.
$| = 1;
turns off buffering for STDOUT[1].
$ perl -e'$| = 1; print("abc\n"); warn("def\n");' 2>&1 | cat
abc
def
- Actually, the currently
select
ed handle, which is the oneprint
writes to if no handle is specified, which is STDOUT by default.
How do I read and write large buffers to a process stdin/stdout/stderr in Perl?
Since no one managed to make this work I've looked harder in CPAN and found the IPC::Run
module.
use IPC::Run;
sub run_prog {
my ($in, $t, $cmd, @args) = @_;
my ($out, $err);
IPC::Run::run([$cmd, @args], \$in, \$out, \$err, IPC::Run::timeout($t));
return ($?, $out, $err);
}
Getting STDOUT, STDERR, and response code from external *nix command in perl
Actually, the proper way to write this is:
#!/usr/bin/perl
$cmd = 'lsss';
my $out=qx($cmd 2>&1);
my $r_c=$?;
print "output was $out\n";
print "return code = ", $r_c, "\n";
You will get a '0' if no error and '-1' if error.
Perl: retrieve output from process in IPC::Run if it dies
As noted by @ysth, the reason you do not get any output, is that the STDOUT
and STDERR
of the process corresponding to the command $cmd
, is not line buffered, but rather block buffered. So all output is collected in a buffer which is not shown (printed) until the buffer is full or it is explicitly flushed. However, when your command times out, all the output is still in the buffer and has not yet been flushed and hence collected into the variable $out
in the parent process (script).
Also note that since your $cmd
script is a Perl script, this behavior is documented in perlvar
:
$|
If set to nonzero, forces a flush right away and after every write
or print on the currently selected output channel. Default is 0
(regardless of whether the channel is really buffered by the system or
not; $| tells you only whether you've asked Perl explicitly to flush
after each write). STDOUT will typically be line buffered if output is
to the terminal and block buffered otherwise.
The problem (that the program is not connected to a terminal or a tty) is also noted in the documentation page for IPC::Run
:
Interactive applications are usually optimized for human use. This can
help or hinder trying to interact with them through modules like
IPC::Run. Frequently, programs alter their behavior when they detect
that stdin, stdout, or stderr are not connected to a tty, assuming
that they are being run in batch mode. Whether this helps or hurts
depends on which optimizations change. And there's often no way of
telling what a program does in these areas other than trial and error
and occasionally, reading the source. This includes different versions
and implementations of the same program.
The documentation also lists a set of possible workarounds, including using pseudo terminals.
One solution for your specific case is then to explicitly make STDOUT
line buffered at the beginning of your script:
STDOUT->autoflush(1); # Make STDOUT line buffered
# Alternatively use: $| = 1;
for (my $i = 0; $i < 10; $i++) {
sleep 1;
print "Hello from script 1 " . localtime() . "\n";
}
Edit:
If you cannot modify the scripts you are running for some reason, you could try connect the script to a pseudo terminal. So instead of inserting statements like STDOUT->autoflush(1)
in the source code of the script, you can fool the script to believe it is connected to a terminal, and hence that it should use line buffering. For your case, we just add a >pty>
argument before the \$out
argument in the call to harness
:
my $h = harness $cmd, \undef, '>pty>', \$out,
timeout(12, exception => {name => 'timeout'});
eval {
run $h;
};
Related Topics
How to Close a Non-Blocking Socket
Why Using a Uimage Instead of a Zimage
Accessing Data Appended to an Elf Binary
Where Are Gdb Symbols Coming From
How to Use .Notparallel in Makefile Only on Specific Targets
How to Tie a Network Connection to a Pid Without Using Lsof or Netstat
Which Perf Events Can Use Pebs
Convert Animated Gif to Video on Linux Server While Preserving Frame Rate
Interprocess Communication via File
Putting Two Consecutive Lines into One Line with Perl/Awk
How to Rename Files in Bash to Increase Number in Name
Is There a Point to Trapping "Segfault"
Curl Progress Bar: How to Pipe and Extract Numbers Only Using Grep
How to Get Notified for Ip Address Changes Automatically
Using Linux Cut, Sort and Uniq
Injecting Code into Executable at Runtime
How to Overcome an Incompatibility Between the Ksh on Linux VS. That Installed on Aix/Solaris/Hpux