Reputation: 39
I have a piece of code that is formed through string processing and I am trying to run an eval to capture the output of the code into another string. The piece of code is formed some steps itself, which means it's not in body of Perl program in one piece. This is why I am using eval.
I tried using Tiny::Capture
without capture
, capture_merged
, capture_stdout
and also Safe
with Safe->new->reval()
.
use strict;
use warnings;
use Capture::Tiny 'capture';
use Capture::Tiny 'capture_stdout';
use Capture::Tiny 'capture_merged';
# $num_loops is defined inside main code body.
my $num_loops = 10;
#In reality, new code is formed through a series of steps.
my $new_code = "foreach \$idx (0..\$num_loops-1) {print \"I am at iteration number \$idx\n\";}";
my $out = capture_merged {eval $new_code;};
print $out;
I'm using Perl 5.30.
The piece of code above isn't printing anything out. Neither does it print any error in log. I have given intermediate prints in original code to make sure the string thrown into eval has real code, so that's not an issue.
What is the issue in the code above?
Upvotes: 1
Views: 1464
Reputation:
If I understand you correctly, you want to do the same as the
$var = `... commands ...`;
$var = qx(.. commands ..);
but with commands
being perl instead of shell code, and without forking a separate process.
You can do that by localizing STDOUT
and re-opening the local filehandle as an in-memory file.
Simple example, without any external module:
use strict;
sub qp(&){
my $s;
open local *STDOUT, '>', \$s or die "open in-memory file: $!";
&{$_[0]};
die $@ if $@;
$s;
}
# Usage:
my $a = qp { eval 'print "in"' };
print "<$a>\n";
print "out\n";
# 'in' will go into the string, 'out' to the actual stdout
# Use with your example (fixed to use 'my $idx'):
my $num_loops = 10;
my $new_code = "foreach my \$idx (0..\$num_loops-1) {print \"I am at iteration number \$idx\n\";}";
my $out = qp { eval $new_code };
print $out;
The limitations of this are
syswrite
output -- that could be easily fixed by tie
ing the localized STDOUT
and implementing PRINT
, PRINTF
and WRITE
to append to a string.STDOUT
to a temporary file (that's what Capture::Tiny
does under the hood).In your eval'd code, you're using foreach $idx (...)
without $idx
being defined, and since you're within the scope of use strict
, that errors out, and your code is not run. But you're not checking the value of $@
, and you're not able to see that error. Change it to foreach my $idx (...)
as I did above.
Upvotes: 0
Reputation: 6808
Documentation is written for a reason, you should not assume return value from a function -- documentation gives details what is a return value.
Sample code how you could use eval
for your situation.
use strict;
use warnings;
my $num_loops = 10;
my $new_code = '
my $r;
$r .= "I am at iteration number $_\n" for 0..$num_loops-1;
return $r;
';
my $out = eval $new_code;
print $out;
Output
I am at iteration number 0
I am at iteration number 1
I am at iteration number 2
I am at iteration number 3
I am at iteration number 4
I am at iteration number 5
I am at iteration number 6
I am at iteration number 7
I am at iteration number 8
I am at iteration number 9
Upvotes: 1