捕获Perl eval中的变量赋值

时间:2011-11-23 23:28:20

标签: perl

我希望能够从Perl eval中捕获变量赋值。也就是说,确定在代码中分配了哪些变量名称并提取它们的值。

例如,如果我跑:

eval '$foo=42; $bar=3.14;'

eval的结果是3.14(评估的最后一个值),但我还希望能够确定名称“$ foo”和“$ bar”及其值(事先不知道名称)。

我已经阅读了几种通过Safe和Eval :: Context将变量插入eval块的方法,但还没有提取它们的任何方法。我更熟悉Python的eval / exec,它已经内置了对它的支持。

3 个答案:

答案 0 :(得分:7)

在eval结束后,eval中声明的任何词法变量都将丢失。要捕获和隔离在eval中设置的全局变量,可以考虑使用Safe模块来创建新的全局命名空间。如下所示:

use Safe;

my $vars = Safe->new->reval(qq{
    $code_to_eval; 
    $code_to_search_the_symbol_table_for_declared_variables
});

将搜索代码定义为遍历嵌套%main::符号表以搜索任何感兴趣变量的内容。你可以让它返回一个包含这些信息的数据结构,然后你可以随意使用它。

如果您只担心在根级别定义的变量,您可以编写如下内容:

use strict;
use warnings;

my $eval_code = '$foo=42; $bar=3.14;';

use Safe;
my $vars = Safe->new->reval(
    $eval_code . q{;
    my %vars;
    for my $name (keys %main::) {
        next if $name =~ /::$/    # exclude packages
         or not $name =~ /[a-z]/; # and names without lc letters

        my $glob = $main::{$name};
        for (qw($SCALAR @ARRAY %HASH)) {
            my ($sigil, $type) = /(.)(.+)/;
            if (my $ref = *$glob{$type}) {
                $vars{$sigil.$name} = /\$/ ? $$ref : $ref
            }
        }
    }
    \%vars
});

print "$_: $$vars{$_}\n" for keys %$vars;
# $foo: 42
# $bar: 3.14

搜索代码也可以使用Padwalker使用peek_my函数搜索任何已定义变量的当前词法范围。

答案 1 :(得分:1)

正如Eric Strom所建议的那样,我试图根据Safe充实解决方案。

package main;
use warnings; use strict;
use Safe;

my $cpt = new Safe;

$cpt->permit_only(qw(sassign lineseq padany const rv2sv leaveeval));
my $name_space = $cpt->root;

my $no_strict = 0;
#
# populate the clean symbol table
#
$cpt->reval('0');
die "safe compartment initialisation error: $@" if $@;
my %symtab_clean = do {no strict 'refs'; %{$name_space.'::'} }; 

my $result = $cpt->reval('$foo=42; $bar=3.14;', $no_strict);

if ($@) {
    warn "eval error: $@";
}
else {
    #
    # symbol table additions
    #
    my %symtab_dirty = do {no strict 'refs'; %{$name_space.'::'} }; 

    my @updated_variables = grep { ! exists $symtab_clean{$_} } (sort keys %symtab_dirty);

    foreach my $variable (@updated_variables) {
        my $value = do{ no strict 'refs'; ${$name_space.'::'.$variable} };
       print "variable $variable was set to: $value\n"
    }
}

注意:

  1. 以上允许最小限制的saf操作码组。请参阅perl opcodes
  2. 我选择在执行命令
  3. 之前和之后查找差异

答案 2 :(得分:0)

好吧,eval运行后,$ foo的值将为42。使$ foo不可见的范围没有变化。

但这当然是假设$ foo存在的预知。

在任何情况下,这只能在没有use strict设置的情况下工作,这几乎肯定是一个坏主意。在use strict到位的情况下,脚本将无法编译。我假设您的示例更复杂,并且您正在寻找对eval执行的现有哈希值的更改。但是不清楚你的eval如何不知道它正在发生什么变化,除非正在进行某种动态代码注入。