为什么在使用`do`函数时无法加载Perl库?

时间:2010-01-10 11:26:08

标签: perl subroutine

我是Perl的新手,我正在更新一个旧的Perl网站。每个.pl文件似乎都在顶部有这一行:

do "func.inc";

所以我想我可以使用这个文件来标记子程序以供全局使用。

func.inc

#!/usr/bin/perl
sub foobar
{
    return "Hello world";
}

index.pl

#!/usr/bin/perl
do "func.inc";
print "Content-type: text/html\n\n";
print foobar();

然而,我收到此错误:

Undefined subroutine &main::foobar called at /path/to/index.pl line 4.

这两个文件都在同一个目录中,并且func.inc中已经有了整个网站使用的潜在音调。但是,该脚本适用于Linux生产环境,但不适用于我的Windows 7开发环境(我正在使用ActivePerl)。

更新

看起来文件没有被包含在内;如果使用绝对路径包含文件,则子工作...

do "C:/path/to/func.inc";

...所以看起来相对路径不适用于我的本地开发环境,但它们可以在生产环境中工作。但这对我没有好处,因为我的开发机器上的绝对路径不适用于实时服务器。

如何在Windows 7开发计算机上使用相对路径让do工作?

更新2:

我使用的是Perl -T开关。不幸的是,这删除了“。”来自@INC,因此阻止我们使用do的相对路径。我删除了此开关,旧代码现在正在运行。我知道这不是一个好习惯,但不幸的是我正在处理旧代码,所以我似乎别无选择。

5 个答案:

答案 0 :(得分:6)

perlfunc documentation for do读取

  
      
  • 执行EXPR
      使用EXPR的值作为文件名,并以Perl脚本的形式执行文件的内容。

    do 'stat.pl';
    
         

    就像

    eval `cat stat.pl`;
    
         

    除了它更有效和简洁,跟踪错误消息的当前文件名,搜索@INC目录,并在找到文件时更新%INC

  •   

所以要看到所有这些,请说C:\Cygwin\tmp\mylib\func.inc看起来像

sub hello {
  print "Hello, world!\n";
}

1;

我们在以下程序中使用它:

#!/usr/bin/perl

use warnings;
use strict;

# your code may have unshift @INC, ...
use lib "C:/Cygwin/tmp/mylib";

my $func = "func.inc";

do $func;

# Now we can just call it. Note that with strict subs enabled,
# we have to use parentheses. We could also predeclare with
# use subs qw/ hello /; 
hello();

# do places func.inc's location in %INC
if ($INC{$func}) {
  print "$0: $func found at $INC{$func}\n";
}
else {
  die "$0: $func missing from %INC!";
}

它的输出是

Hello, world!
./prog: func.inc found at C:/Cygwin/tmp/mylib/func.inc

正如您所观察到的,do并非总是没有水晶楼梯,do文档解释了这一点:

  

如果do无法读取该文件,则会返回undef并将$!设置为错误。如果可以读取文件但无法编译,则返回undef并在$@中设置错误消息。如果文件编译成功,do将返回最后一个表达式的值。

要检查所有这些情况,我们不能再使用do "func.inc"而是

unless (defined do $func) {
  my $error = $! || $@;
  die "$0: do $func: $error";
}

每个案例的解释如下。

do无法读取文件

如果我们将func.inc重命名为nope.inc并重新运行该计划,我们就会

./prog: do func.inc: No such file or directory at ./prog line 12.

do可以读取文件但无法编译

nope.inc重命名为func.inc并删除hello中的结束大括号,使其看起来像

sub hello {
  print "Hello, world!\n";

1;

现在运行程序,我们得到

./prog: do func.inc: Missing right curly or square bracket at C:/Cygwin/tmp/mylib/func.inc line 4, at end of line
syntax error at C:/Cygwin/tmp/mylib/func.inc line 4, at EOF

do可以读取文件并对其进行编译,但它不会返回真值。

删除1;末尾的func.inc以使其成为

sub hello {
  print "Hello, world!\n";
}

现在输出

./prog: do func.inc:  at ./prog line 13.

因此,如果没有返回值,成功就像失败一样。我们可能会复杂检查do结果的代码,但更好的选择是始终在Perl库和模块的末尾返回一个真值。

请注意,即使启用了污点检查(-T),程序也能正常运行。试试看吧!请务必阅读Taint mode and @INC in perlsec

答案 1 :(得分:3)

您使用子程序的方式与使用任何其他子程序的方式相同。用do加载它并不重要。但是,您不应该使用do。查看Intermediate Perl中的“软件包”一章,了解从其他文件加载子程序的详细说明。简而言之,请改用require

请参阅do的文档。您需要 func.inc (您也可以在 func.pl 中调用 func.pl ,因为 pl 是“perl库”) Perl将查找库的目录。这可能与 index.pl 的目录不同。将 func.inc 放在@INC的某处,或将其目录添加到@INCdo如果无法加载文件也不会死亡,因此它不会告诉您它失败了。这就是为什么你不应该使用do来加载库。 :)

答案 2 :(得分:2)

确保路径正确,请使用:


#!/usr/bin/perl 
require("func.inc");
print "Content-type: text/html\n\n"; 
print foobar(); 

答案 3 :(得分:1)

我首先检查文件是否实际加载,documentation for do提及如果找到文件,它会更新%INC。文档中还有更多信息。

答案 4 :(得分:0)

确保您在正确的路径中有func.inc。

do "func.inc" 

表示您说func.inc与perl脚本位于同一路径中。检查正确的路径,然后执行此操作

do "/path/func.inc"