Deck
Deck

Reputation: 1979

How to redirect STDOUT and STDERR to a variable

I want to redirect STDERR and STDOUT to a variable. I did this.

close(STDOUT);
close(STDERR);

my $out;
open(STDOUT, ">>", \$out);
open(STDERR, ">>", \$out);

for(1..10)
{
    print "print\n"; # this is ok. 
    warn "warn\n"; # same
    system("make"); # this is lost. neither in screen nor in variable.
}

The problem with system. I want the output of this call to be captured too.

Upvotes: 16

Views: 20450

Answers (5)

mndrix
mndrix

Reputation: 3198

There are several ways to redirect and restore STDOUT. Some of them work with STDERR too. Here are my two favorites:

Using select:

my $out;
open my $fh, ">>", \$out;
select $fh;
print "written to the variable\n";
select STDOUT;
print "written to original STDOUT\n";

Using local:

my $out;
do {
    local *STDOUT;
    open STDOUT, ">>", \$out;
    print "written to the variable\n";
};
print "written to original STDOUT\n";

Enjoy.

Upvotes: 6

HoldOffHunger
HoldOffHunger

Reputation: 20948

TLDR Answer

use Capture::Tiny;

Merged STDOUT and STDERR

If you want STDOUT (from print()s) and STDERR (from warn()s) to be merged, then use...

my ($merged,  @result) = capture_merged { print "Hello, world!" };  # static code
my ($merged,  @result) = capture_merged { eval $codetoeval };       # code in variable

Separated STDOUT and STDERR

If you want them separated...

my ($stdout, $stderr, @result) = capture { print "Hello, world!" };   # static code
my ($stdout, $stderr, @result) = capture { eval $codetoeval };        # code in variable

Results of Eval

@result indicates the success, with success being [1], and failure being []. Tiny has a ton of other functions that you can look through for other cases, like code references, etc.. But I think the code above should cover most of any Perl developer's needs.

Upvotes: 1

salva
salva

Reputation: 10242

use Capture::Tiny!

Upvotes: 13

Dallaylaen
Dallaylaen

Reputation: 5318

Why not use IPC::Open3?

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 754920

Are you seeking to capture the output in a variable? If so, you have use backticks or qx{} with appropriate redirection. For example, you could use:

#/usr/bin/env perl
use strict;
use warnings;

# Ensure we have a way to write messages
open my $fh, '>', "output" or die;

close(STDOUT);
close(STDERR);

my $out;
open(STDOUT, ">>", \$out) or do { print $fh, "failed to open STDOUT ($!)\n"; die };
open(STDERR, ">>", \$out) or do { print $fh, "failed to open STDERR ($!)\n"; die };

foreach my $i (1..10)
{
    print "print $i\n"; 
    warn "warn $i\n";
    my $extra = qx{make pth$i 2>&1};
    print $fh "<<$i>><<$out>><<$extra>>\n";
}

(I happen to have programs pth1, pth2 and pth3 in the directory - they were made OK; pth4 and above write errors to stderr; the redirection was necessary.)

You should always check the success of operations such as open().

Why is this necessary? Because writing to a variable requires the cooperation of the process doing the writing - and make doesn't know how to cooperate.

Upvotes: 6

Related Questions