tags:

views:

2029

answers:

6

I would like to send the output from a command to both STDOUT and to a variable. I want to combine:

my $var = `some command` ;   
system( 'some command' ) ;

[Tee][1] is a step in the right direction but this sends it to a file rather than to a variable. I guess I could then read the file but it would be simpler to get it straight there.

[1]: http://search.cpan.org/~dagolden/Tee-0.13/lib/Tee.pod Tee

+11  A: 

Does the output to both streams have be simultaneous?

If not, you could do:

my $var = 'cmd'
my $output = `$cmd`
print STDOUT $output

or for a safer version, which doesn't involve invoking a subshell, and prints to STDOUT a line at a time:

sub backtick(@)
{
    my $pid = open(KID, '-|');
    die "fork: $!" unless defined($pid);
    if ($pid) {
        my $output;
        while (<KID>) {
            print STDOUT $_;
            $output .= $_; # could be improved...
        }
        close(KID);
        return $output;
    } else {
        exec @_;
    }
}

my @cmd = ('/bin/ls', '-l');
my $output = backtick(@cmd);
Alnitak
Yes - I would like hte output to STDOUT to be simulataneous - it might be quite long and it would give the user a warm feeling htat something is happening.
justintime
ok, newest edit does that - albeit line-buffered.
Alnitak
A: 

Send the output from the Tee module to /dev/stdout (or /dev/fd/1).

Jonathan Leffler
That assumes that such a thing exists on the target OS, and there's no indication that it does.
brian d foy
Well, yes, it does make that assumption, and if the o/s does not support it, then no, this answer does not apply. But where the /dev/stdout facility is available, it makes it trivial to apply Tee.
Jonathan Leffler
+2  A: 

You could use the IO::String module to select() STDOUT to a string and then call system() to run the command. You can collect the output from the IO::String handle. This effectively does what the backtick syntax does.

So to gather command output realtime, run the system() command asynchronously through fork() or some other means and poll the handle for updates.

EDIT: Per OP, it turns out this approach does not work. select() doesn't affect system() calls.

Also, IO::String has been replaced with new open() syntax since Perl 5.8 that does the same function.

spoulson
Since perl 5.8 you can open a FH directly to a variable, e.g., open(my $fh, ">" \my $variable) or die "Err: $!".So no more need for IO::String (or IO::Scalar, etc.)
runrig
Learn something new every day...
spoulson
It seems that select doesn't redirect output from "system" which is a pity.
justintime
+2  A: 

Perhaps my answer here can help you: How can I hook into Perl’s print?

Axeman
I can't see how - it's not Perl that's doing the output, it's the child process.
Alnitak
+1  A: 

You can do this through a file handle as well. Not as elegant as some solutions, but it would likely work. Something along the lines of:

my $foo;
open(READ, "env ps |");
while (<READ>) {
    print;
    $foo .= $_;
}
print $foo;
close(READ);
Jack M.
+3  A: 

You want Capture::Tiny

use Capture::Tiny 'tee';
my $output = tee { system( "some command" ) };

I wrote it to replace Tee and about 20 other modules that do some sort of capturing but are flawed in one way or another.

-- xdg (aka dagolden)

xdg