在Perl eval中捕获变量赋值

5
我希望能够捕获Perl eval中的变量赋值。也就是说,确定代码中已分配哪些变量名称并提取它们的值。
例如,如果我运行:
eval '$foo=42; $bar=3.14;'

eval的结果是3.14(最后一个被评估的值),但我也想能够确定"$foo"和"$bar"的名称及其值(不事先知道名称)。
我已经阅读了一些将变量插入eval块的方法,通过Safe和Eval::Context,但尚未找到任何提取它们的方法。我更熟悉Python的eval/exec,它们内置支持此功能。
3个回答

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函数搜索当前词法作用域中定义的任何变量。

Perl从哪个版本开始允许从字符串中访问glob slots?我记得以前尝试这样做时会出现“不是哈希”错误。所以,我想我可能错过了某个增量页面上的更新。 - Axeman

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. 以上允许一组最小限制的安全操作码。请参见perl opcodes
  2. 我选择在执行命令之前和之后查找差异

0

好的,eval 运行后,$foo 的值将为 42。没有任何改变作用域的情况会使 $foo 不可见。

但是这当然假定你预先知道了 $foo 的存在。

无论如何,在没有设置 use strict 的情况下才能正常工作,这几乎肯定是一个坏主意。如果使用了 use strict,脚本将无法编译。我假设你的示例更复杂,并且你正在寻找对现有哈希表所做的更改,由你的 eval 执行。但是,除非存在某种动态代码注入,否则不清楚你的 eval 如何不知道它正在更改什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接