2011-11-23 5 views
5

Me gustaría poder capturar asignaciones variables de una evaluación Perl. Es decir, para determinar qué nombres de variables se han asignado dentro del código y extraer su valor.Asignaciones de variables de captura en Perl eval

Por ejemplo si funciono:

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

El resultado de la función eval es de 3.14 (el último valor evaluado), pero también me gustaría ser capaz de determinar los nombres de "$ foo" y "$ bar "y sus valores (sin saber los nombres de antemano).

He leído un par de formas de insertar variables en el bloque eval, a través de Safe y Eval :: Context, pero todavía no hay ninguna forma de extraerlas. Estoy más familiarizado con el eval/exec de Python, que se ha integrado en el soporte para esto.

Respuesta

1

Aquí está mi intento de dar cuerpo a una solución basada en Safe, como lo sugirió Eric Strom.

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: [email protected]" if [email protected]; 
my %symtab_clean = do {no strict 'refs'; %{$name_space.'::'} }; 

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

if ([email protected]) { 
    warn "eval error: [email protected]"; 
} 
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" 
    } 
} 

Notas:

  1. Lo anterior permite un conjunto restrictve mínima de opcodes saf. Ver perl opcodes
  2. que he elegido a buscar las diferencias antes y después de ejecutar el comando
0

Bueno, el valor de $ foo será 42 después de que se ejecute eval. No hay ningún cambio en el alcance que hace que $ foo sea invisible.

Pero eso por supuesto asume el conocimiento previo de la existencia de $ foo.

En cualquier caso, esto solo funcionará sin el conjunto use strict, lo cual es casi seguro que una mala idea. Con use strict en su lugar, la secuencia de comandos no se compilará. Supongo que su ejemplo es más complejo y está buscando cambios en un hash existente realizado por su eval. Pero no está claro cómo su eval no pudo saber lo que estaba cambiando, a menos que haya algún tipo de inyección de código dinámico.

7

Cualquier variable léxica declarada dentro de una evaluación se perderá después de que finalice la evaluación. Para capturar y aislar las variables globales que se establecen dentro de un eval, se puede buscar utilizando el módulo Safe para crear un nuevo espacio de nombres global. Algo así como lo siguiente:

use Safe; 

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

Cuando el código de búsqueda se define como algo que camina la tabla anidada %main:: símbolo de la búsqueda de las variables de interés. Puede hacer que devuelva una estructura de datos que contenga la información, y luego puede hacer con ella lo que quiera.

Si sólo está preocupado por las variables definidas en el nivel raíz, se podría escribir algo como:

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 

El código de búsqueda también se podría emplear Padwalker para buscar el ámbito léxico actual de las variables definidas utilizando el peek_my función.

+0

¿Qué versión se inició Perl que permite el acceso a las ranuras glob de las cadenas? Podría haber jurado que solía obtener un error "no es un hash" intentando hacer algo así. Entonces, creo que me he perdido eso en alguna página delta. – Axeman

Cuestiones relacionadas