Инициализация переменных perl с использованием eval - PullRequest
6 голосов
/ 22 сентября 2011

Я предполагаю, что это должно быть что-то очевидное для тех, кто знает Perl, но я просто не понимаю ... Я также думаю, что это связано с проблемами, описанными в Perl scoping «darkness - но Я не могу применить что-либо из этого в моем случае.

В любом случае, вот код:

#!/usr/bin/env perl
# call with:
# ./test.pl

use strict;

my $tvars = "my \$varA = 1;
my \$varB = 2;
my \$varC = 3;
";

my @lines = split /\n/, $tvars;
foreach my $line (@lines) {
  print "$line\n";
  eval $line; warn $@ if $@;
}

#~ print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 18.
#~ print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 19.
#~ print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 20.

$tvars = "our \$varA = 1;
our \$varB = 2;
our \$varC = 3;
";

@lines = split /\n/, $tvars;
foreach my $line (@lines) {
  print "$line\n";
  eval $line; warn $@ if $@;
}

print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 33.
print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 34.
print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 35.

Проще говоря, я хотел бы, чтобы что-то вроде "$varA = 1;" было записано в виде строки (текстовый файл); и я хотел бы от perl до eval, чтобы впоследствии у меня был доступ к переменной "$varA" в том же скрипте - ошибки, которые я получаю, когда пытаюсь получить доступ к ним после eval, находятся в комментарии кода выше (однако, в течение eval предупреждений не поступало). ( Я предполагаю, что мне нужно что-то вроде «глобальных» переменных, если eval работает в другом контексте, чем основной сценарий? )

Как бы я поступил так? Должен ли я пройти через весь этот бизнес по определению пакетов, даже для простого сценария, подобного описанному выше?

Большое спасибо заранее за любые ответы,
Ура!

Ответы [ 3 ]

13 голосов
/ 22 сентября 2011

Все это связано со сферой действия.Переменные объявлены с my внутри выражения eval.Это делает их локальными для оператора eval и недоступными после выхода из оператора eval.Сначала вы можете объявить их, хотя:

my ($varA, $varB, $varC);  # declare outside the eval statement

my $tvars = "\$varA = 1;
\$varB = 2;
\$varC = 3;
";

eval $tvars;
# local $varA, $varB, $varC variables are now initialized

или, как вы предлагаете, вы можете использовать глобальные переменные.Самый простой (хотя и не обязательно «лучший» способ) состоит в добавлении :: ко всем именам переменных и получении их в основном пакете.

my $tvars = "\$::varA = 1;
\$::varB = 2;
\$::varC = 3;
";

eval $tvars;
print "A=$::varA, B=$::varB, C=$::varC\n";

Теперь, когда вы попробовали our переменные в вашем примеревы фактически инициализировали пакетные (глобальные) переменные.Но вне оператора eval вам по-прежнему необходимо указать (т.е. указать имя пакета) их для доступа к ним:

$tvar = "our \$foo = 5";
eval $tvar;

print $main::foo;    # ==> 5
2 голосов
/ 22 сентября 2011

Проблема в том, что когда вы делаете eval $string, $string оценивается как его собственная подпрограмма, которая имеет свою собственную лексическую область видимости.От perldoc -f eval:

In the first form [in which the argument is a string], the return value of EXPR is parsed and 
executed as if it were a little Perl program. The value of the expression (which is itself 
determined within scalar context) is first parsed, and if there were no errors, executed in the 
lexical context of the current Perl program, so that any variable settings or subroutine and format 
definitions remain afterwards.

Итак, другими словами, если у вас есть:

use strict;
use warnings;
eval "my $foo=5;";
print "$foo\n";

, вы получите ошибку:

Global symbol "$foo" requires explicit package name at -e line 3.
Global symbol "$foo" requires explicit package name at -e line 4.

Однако, если вы сначала инициализируете свои переменные, все в порядке.

use strict;
use warnings;
my $foo;
eval "\$foo=5;";
print "$foo\n"; #prints out 5, as expected.
0 голосов
/ 15 февраля 2013

jjolla, вы можете использовать require $filename; или require "filename";, чтобы включить файл с синтаксисом perl.это объявит любые переменные, которые вам нужны, как глобальные.Но, как всегда, будьте осторожны с глобалами.

...