[Perl]关于use的执行时机
最近遇到的一个任务是要将散落在系统各个文件中的 use lib '/opt/mysoft/lib'
; 这样的绝对路径引用改成相对路径,
以便整个系统能够轻易地移动到其他的目录中。系统是工作在Apache2 + mod_perl 下的,cgi程序位于 /opt/mysoft/cgi-bin 下。
初看起来似乎很容易:
use lib '../lib'; # 错误!
但实际尝试一下就会发现,这里的 ‘..’ 似乎找错了位置。当前目录并不是cgi程序所在的 /opt/mysoft/cgi-bin。
经过一番实验后,发现在 use lib
时,当前目录居然是在 / 下!这样这条路就走不通了。
另一个想法是通过 $0
获取到CGI程序的路径,再根据此路径来找到 lib 目录不就可以了么?
$dir = dirname $0;
use lib "$dir/../lib"; # 错误!
print "$_\n" for @INC;
实际运行就会发现,这一招也行不通。原因是use lib是在编译时执行的。编译时代码并没有被执行,
因此 $dir
变量为空(甚至都不存在),于是就成了 use lib "/../lib"
,当然不正确了。
这样看来 use lib
似乎必须要书写完整路径了。
CPAN文档的解释如下:
lib - manipulate @INC at compile time
实际上 use lib
可以理解为 BEGIN { unshift @INC, '/opt/mysoft/lib'; }
。
BEGIN段的内容在这一段编译结束后立即执行。
回到本来的问题上,如何能避免在系统中使用绝对路径? 其实使用mod_perl的功能一次性地指定好@INC,那么在CGI和pm模块中就无需再use lib了。
在mod_perl的官方文档中有关于如何调整@INC的说明。
一种方法是在 startup.pl 中书写完整路径:
use lib '/opt/mysoft/lib';
另一种方法是直接在httpd.conf中修改@INC
:
PerlSwitches -I/opt/mysoft/lib
这样虽然不能完全避免绝对路径,但至少在产品代码中可以不再使用 use lib 了。
同样地,use 也是在编译时执行的。(参考)use Module;相当于 BEGIN { require Module; Module->import( LIST ); }
程序代码中的所有use指令都会在代码执行前被执行,因此use不论放在哪里,效果都是一样的。因此 if ($use_first_lib) { use FirstLib; … } else { use SecondLib; … }
这样的代码是毫无用处的,不论$use_first_lib的值如何,FirstLib和SecondLib都会被加载。