在perl中声明全局变量

时间:2013-12-16 21:03:11

标签: perl global-variables perl-module

我试图了解use vars ($var);的工作原理。我意识到如果我想全局声明一个变量,我可以使用use vars ($var);或Perl 5.6以及更高版本我可以使用our $var;

然而,我仍然只是想了解它是如何工作的。查看the source,看起来您只是将变量声明为typeglob并将其设置为等于其自身的引用版本。什么?!

# My_Module.pm

use strict (vars, subs);
use CGI::Carp;

*My_Module::My_Global = \$My_Module::My_Global;

sub my_function {

  $My_Global = 'Am I declared?';

}

现在这不起作用,我显然得到一个'未定义的符号'错误。那么use vars如何做呢?

---------------- UPDATE ---------------

我确实完全复制了你的代码,用test use X;而不是shell来运行它...

X.pm

use strict;
BEGIN { package X; no strict qw( refs ); *main::x = \${"main::x"}; }
print "Content-type: text/html\n\n"; # I added this line since not printing to shell
$x = 123; say $x;
1; 

test.cgi

#!/usr/bin/perl
use strict;
use X;

...我之前遇到了相同的编译错误:

Global symbol "$x" requires explicit package name at X.pm line 4.

这只适用于shell而不是模块吗?

2 个答案:

答案 0 :(得分:3)

分配对typeglob的引用是一种特殊的赋值;它仅使用引用替换引用类型的typeglob部分。此外,vars使用符号引用作为变量名称,而不是。 use vars也在不同的包中执行,并且在编译时,而不是运行时。内联等价物将是:

BEGIN { package foo; *My_Module::My_Global = \${"My_Module::My_Global"} }

也就是说,使用全局变量通常是一个坏主意。使用不再支持的perl版本同样是个坏主意。

答案 1 :(得分:1)

只需使用变量就可以将其创建为(全局)包变量。

$ perl -wE'$x = 123; say $x;'
123

这非常危险,因此程序员通过使用use strict qw( vars );告诉Perl禁止这样做。

$ perl -wE'use strict; $x = 123; say $x;'
Global symbol "$x" requires explicit package name at -e line 1.
Global symbol "$x" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.

但是,为了保持其有用性,严格的变量允许使用导入的变量而不会出错。

$ perl -wE'use strict; say $Config{version};'
Global symbol "%Config" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.

$ perl -wE'use strict; use Config qw( %Config ); say $Config{version};'
5.18.1

use vars只是创建一个新变量并将其导出。


虽然你的代码接近这样做,但它有两个问题。

  1. 在代码中遇到对变量的任何引用之前,导入变量至关重要。需要进行两项更改才能在代码中修复此问题。

    1. 在评估赋值之前,必须编译赋值的操作数,因此在代码引用变量之前,您的赋值无法导入变量。

      *Package::foo = $Package::foo;      # XXX Compile-time lookup
      *Package::foo = ${"Package::foo"};  # Runtime looup
      *Package::foo = \( my $anon );      # Would work too.
      
    2. 在导入变量之前等待整个脚本编译,因此在严格已经禁止对变量的引用之后进行导入。您需要使用BEGIN { } [1] 更快地执行导入。

  2. 对于要考虑导入的变量,执行导出的代码必须编译在与导出变量的包不同的包中。

  3. 如果我们应用这些修补程序,我们最终会得到以下结果:

    $ perl -wE'
       use strict;
       BEGIN { package X; no strict qw( refs ); *main::x = \${"main::x"}; }
       $x = 123; say $x;
    '
    123
    

    注意:

    1. 请记住

      use Module qw( ... );
      

      基本相同
      BEGIN { require Module; Module->import(qw( ... )); }
      

      换句话说,执行模块并在解析任何其他代码之前调用其import方法。