Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Redirecting output into a variable 2

Status
Not open for further replies.

TonyGroves

Programmer
Aug 13, 2003
2,389
IE
When I run a system command from a Perl script, is there any simple way to redirect standard and/or error output into a variable(s)? Obviously I could redirect to a file, and then read in that file, but I'm hoping there's a more efficient way than that.

I'm running Perl on Windows, in case that's relevant.

Any ideas?

Thanks.
 
Instead of
Code:
system "some_command.exe";
try
Code:
my $var = `some_command.exe`;
The back-tick characters are on the same key as the tilde (~).
 
I'm not at all sure about standard error on a windoze box. Under *ix, you'd
Code:
`command 2>&1`;
but that's relying on the shell to feed stderr to the same place as stdout. I tried this under XP
Code:
# file t
print STDOUT "stdout\n";
print STDOUT "stderr\n";

# file u
my $output = `perl t`;
print "Output is [$output]\n";

# file v
my $output = `perl t 2>&1`;
print "Output is [$output]\n";

perl u
stderr
Output is [stdout
]

perl v
Output is [stderr
]
stdout
So it seems as if you can have one or the other but not both. I'm no expert - anyone have chapter and verse on this?

f

"As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs."
--Maurice Wilkes
 
Thanks lads. rharsh's solution works for stdout, but not stderr. fishiface's solution works by sending both outputs to the same variable.
[tt]
d:\tony>echo -----testing----- >test.txt

D:\tony>perl
$var=`type test.txt`;
print "The value of var is \"$var\"\n";
$var=`type usdflguhsg`;
print "The value of var is \"$var\"\n";
$var=`type test.txt 2>&1`;
print "The value of var is \"$var\"\n";
$var=`type usdflguhsg 2>&1`;
print "The value of var is \"$var\"\n";
^Z
The value of var is "-----testing-----
"
The system cannot find the file specified.
The value of var is ""
The value of var is "-----testing-----
"
The value of var is "The system cannot find the file specified.
"

D:\tony>
[/tt]
It would be nice to be able to capture both outputs separately, but there's plenty of food for thought here.
 
You could try (carefully) open3. There are all sorts of problems with this but something like the following might work (untested):
Code:
use IPC::Open3;
use IO::Select;

$pid = open3(*IN, *OUT, *ERR, "mycommand myarg");

$SIG{CHLD} = sub {
    warn "Oops! signalled ($?)" if waitpid($pid, 0) > 0
};

close(IN); # we don't want it waiting for input

$selector = IO::Select->new();
$selector->add(*ERR, *OUT); #watch for outputs here

my( @output, @error );
while (@ready = $selector->can_read) {
    foreach $fh (@ready) {
        push @output, scalar <ERR> if fileno($fh) == fileno(ERR);
        push @output, scalar <OUT> if fileno($fh) == fileno(OUT);
        $selector->remove($fh) if eof($fh);
    }
}
close(CMD_OUT);
close(CMD_ERR);

I really don't know how you'd get on with the signal handling on a non-*ix system but if you're always calling the same command and you're lucky with how it buffers stuff you could be ok.

Let me know,

f

&quot;As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.&quot;
--Maurice Wilkes
 
I figured out this neat little trick while trying to do about the same thing. This works in *nix but I don't know about doze.

Code:
 open(LSOUT, "ls |");
 while(my $line = <LSOUT>)
 {
   print "OMG $line";
 }

 close(SNMP);
[code]

think that works with ERR too.  Cheers.
 
Thanks for all that input.

I decided to write a little function to do the job. Here it is:
Code:
sub system_capture
# Executes a system command, and when it is finished, returns the result code,
# standard output, and error output.
# Arguments: [0] command to be executed.
# Returns a list containing:
#   [0] the command's return code
#   [1] the standard output
#   [2] the error output
# Any redirection specifiers contained in the command will be ignored.
{
  use File::Temp qw(tempfile);
  my(undef,$out)=tempfile(dir=>$ENV{tmp},suffix=>'.tmp');
  my(undef,$err)=tempfile(dir=>$ENV{tmp},suffix=>'.tmp');
  my $rc=system("$_[0] 1>$out 2>$err");
  my $outtext;
  my $errtext;
  if(open(F,"<$out"))
  {
    while(<F>){$outtext.=$_}
    close F;
  }
  if(open(F,"<$err"))
  {
    while(<F>){$errtext.=$_}
    close F;
  }
  return($rc,$outtext,$errtext);
}
It has certain limitations, including speed, but I think it's a neat solution. If anybody can see any way to improve it, suggestions will be welcome.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top