我有这段代码:
#!/usr/bin/perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(get_text);
my $author;
my $text_tmp1 =<<'ENG';
This is a template text
by $author.
ENG
sub get_text {
my $tmp = shift @_;
$author = shift @_;
print "In sub author= $author lang = $tmp \n";
my $final_str = eval('$text_'.$tmp);
print "$final_str \n";
return $final_str;
}
1;
测试脚本:
#!/usr/bin/perl
use strict;
use warnings;
use Modules::TextStuff;
my $str = get_text('tmp1','jim');
print $str;
当我运行测试脚本时,它不起作用。我明白了:
在sub author = jim lang = eng
变量“$ text_tmp1”在(eval 1)第2行不可用。使用 uninitialized值$ final_str连接(。)或字符串
我该如何解决这个问题?
答案 0 :(得分:1)
组合字符串以创建变量名称通常是个坏主意。您可以使用our $text_tmp1 = ...
代替my $text_tmp1 = ...
来挽救当前的程序,但我认为您应该考虑采用不同的方法,例如哈希:
my %templates = (
tmp1 => <<ENG,
This is a template text
by \$author.
ENG
tmp2 => <<ESP,
Esta es templata texta de \$author.
ESP
);
sub get_text {
...
my $final_str = eval( $templates{$tmp} );
...
}
答案 1 :(得分:1)
当eval EXPR
尝试获取确实存在但不再存在的变量的值时,会生成您询问的错误。
>perl -wE"{ my $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in concatenation (.) or string at -e line 1.
<>
请记住,执行文件(例如脚本或模块)是在其自己的词法范围内完成的,就像上面的curlies创建的那样。
可以通过不让它超出范围
来保持变量存活来修复它>perl -wE"my $x = 123; sub f { eval '$x' } say '<'.f().'>';"
<123>
但那对你来说不是一个选择。
其他选项包括使变量成为全局变量。
>perl -wE"{ our $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
<123>
或者强制子捕获它以使它不会停止存在。
>perl -wE"{ my $x = 123; sub f { $x if 0; eval '$x' } } say '<'.f().'>';"
<123>
(if 0
使“void context”警告无效。)
那就是说,看起来你正试图重新发明wheel。不要发明另一个半假的模板系统。
答案 2 :(得分:0)
我正在看几件事:
$text_tmp1
不是包变量。它是词法范围的,因为你用my
声明了它。如果您需要它作为包变量并且在所有子例程中都可见,则需要使用our
声明它。$author
来源,但它未定义。eval
做什么?这在很多层面都是错误的。我将如何做到这一点:
#! /usr/bin/env perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter qw(import);
use Carp;
our @EXPORT_OK = qw(get_text);
our %templates; # This is now a package variable
#
# TEMPLATES
#
$templates{tmp1}=<<TEMPLATE; # We'll use `%s` for replacements
This is a template text
by %s.
TEMPLATE
$templates{tmp2}=<<TEMPLATE;
This is another template and we will substitute
in %s in this one too.
TEMPLATE
sub get_text {
my $template = shift;
my $author = shift;
if ( not exists $templates{$template} ) {
croak qq(Invalid template name "$template");
}
return sprintf $templates{$template}, $author;
}
1;
我会将这些模板中的每一个都作为%templates
哈希中的条目。无需eval
计算模板的变量名称。另请注意,我现在可以实际测试用户是否使用exists传入了有效模板。
另请注意,%template
声明为our
而非my
。这使它在整个包中可用,包括我的包中的任何子程序。
我还使用@EXPORT_OK
代替@EXPORT
。这被认为更有礼貌。您正在请求污染用户命名空间的权限。这就像打开某人的门,问你是否可以喝啤酒而不是闯进冰箱里翻找啤酒。
请注意我如何使用sprintf来处理可替换参数。这再次消除了对eval的需求。
我也更喜欢在我的程序标题上使用#! /usr/bin/env perl
,因为它与Perlbrew等内容更兼容。您正在使用/usr/bin/env
来查找用户路径中的可执行Perl程序。这样,您无需知道它是/bin/perl
,/usr/bin/perl
,/usr/local/bin/perl
还是$HOME/perl5/perlbrew/perls/perl-5.18.0/bin/perl
要使用你的模块,我会这样做:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use Modules::TextStuff qw(get_text);
say get_text('tmp1','jim');
你做的几乎一样的电话。打印出来:
This is a template text
by jim.