如何在不在@INC目录中的目录中“使用”Perl模块?

69

我在脚本的父目录中有一个模块,我想要“使用”它。

如果我这样做:

use '../Foo.pm';

我遇到了语法错误。

我尝试执行:

push @INC, '..';
use EPMS;

而且..显然没有出现在@INC中。

我快疯了!这里出了什么问题?

8个回答

112

use 在编译时发生作用,因此这将起作用:

BEGIN {push @INC, '..'}
use EPMS;

但更好的解决方案是使用use lib,这是一种更好的编写上述内容的方式:

use lib '..';
use EPMS;

如果您的脚本位于不同的目录中运行,建议使用FindBin

use FindBin;                     # locate this script
use lib "$FindBin::RealBin/..";  # use the parent directory
use EPMS;

3
通常你会使用unshift将当前目录添加到列表的最前面,这样Perl在查找模块时就不需要遍历整个@INC列表了。 :) - brian d foy
是的,所以 BEGIN{push}=>use lib 的转换也不完全相同。但我想展示从问题到使其工作所需的最小可用更改。谢谢你注意到了 :) - ephemient
还有PERL5LIB和-I开关,正如Brian提到的那样。 - Axeman
在Windows上,我可以成功地使用以下方法进行调用:use lib 'D:\webserver\sites\test\testing'; 但是不能使用 use lib '\testing';(或 use lib '..\testing'; 或 use lib '.\testing';)。在这两种情况下,我都是从测试文件夹运行脚本。如何进行相对命名?Linux和Windows有区别吗? - msinfo

26

有几种方法可以修改@INC

  • 设置PERL5LIB,如perlrun文档中所述。

  • 使用命令行上的-I选项,也在perlrun文档中有说明。您还可以通过 PERL5OPT 自动应用此选项,但如果要这样做,请使用 PERL5LIB。

  • 在程序中使用use lib,但这种方式不够稳定,因为其他人在另一台机器上可能会将其放在不同的目录下。

  • 手动修改@INC,如果要使用use引入模块,则必须确保在编译时进行修改。不过那太麻烦了。

  • 直接require文件名。虽然这是可能的,但它不允许该文件名加载同一目录中的文件。这肯定会在代码审查中引起注意。


16

就个人而言,我更喜欢将我自己编写的模块(或者是我可以控制的系统所需的模块)放在特定的目录中,并将它们放在一个子目录中。例如:

/www/modules/MyMods/Foo.pm
/www/modules/MyMods/Bar.pm

然后我在哪里使用它们:

use lib qw(/www/modules);
use MyMods::Foo;
use MyMods::Bar;

顺便说一下,当涉及到“推动”时,我更喜欢使用“fat-arrow”逗号:

push @array => $pushee;

但那只是个人偏好的问题。


3
那个推动语法看起来非常混乱,它肯定指错了方向。从机械角度来看,它和逗号一样吗?逗号在方向上更加模糊,因此更好... - Steven Lu
@StevenLu 来自perldoc perlop的说明: “=>运算符是逗号的同义词,但如果它左边的单词以字母或下划线开头,并且仅由字母、数字和下划线组成,则会将其解释为字符串。[...] 否则,根据上下文,=>运算符的行为与逗号运算符或列表参数分隔符完全相同。” =>也被称为“胖逗号”。 - ThisSuitIsBlackNot
很遗憾,<=不适用于此。 - Steven Lu

8
"use lib"是答案,正如@ephemient先生之前提到的那样。另一种选择是使用require/import而不是use,这意味着模块不会在编译时加载,而是在运行时加载。
这样可以允许您修改@INC,就像您尝试的那样,或者您可以传递一个文件路径给require,而不是模块名称。从"perldoc -f require"中了解到:
如果EXPR是裸字(bareword),则require会假定它有一个“.pm”扩展名,并将“::”替换为“/”来帮助您加载标准模块。这种形式的模块加载不会冒险改变您的命名空间。

3

在使用前必须先处理push,并且尽早处理use。因此,我认为您需要一个BEGIN { push @INC, ".."; }来有机会实现。


1
通常情况下,您希望执行unshift操作,以便您的目录位于列表的前面。这样,Perl就不必搜索整个@INC来查找它。 :) - brian d foy
是啊,这个也是...... 我通常不使用相对路径,因为我通常不从固定目录运行脚本,所以...... - Jonathan Leffler

2

据“perldoc -f use”报道:

它与以下代码完全等效:
BEGIN { require Module; import Module LIST; }
不同的是,Module必须是一个裸字。

换句话说,“use”等同于:

  • 在编译时运行,
  • 将包名称转换为文件名称,
  • require该文件名,并
  • import该包。

因此,您可以在BEGIN块内调用require和import而不是调用use:

BEGIN {
  require '../EPMS.pm';
  EPMS->import();
}

当然,如果您的模块在调用导入时不需要实际执行任何符号导出或其他初始化操作,您可以省略该行:
BEGIN {
  require '../EPMS.pm';
}

0

它无法工作的原因是您添加到@INC的内容是相对于命令行中的当前工作目录,而不是脚本目录。

例如,如果您当前在:

a/b/

你正在运行的脚本具有以下URL:

a/b/modules/tests/test1.pl

BEGIN {
    unshift(@INC, "..");    
}

以上意味着..将导致目录a/而不是a/b/modules
要么你必须在你的代码中将..改为./modules,要么在再次运行脚本之前在命令行中执行cd modules/tests

0

一些集成开发环境(IDE)无法正确处理“use lib”这个首选答案。我发现“use lib::relative”可以在我的IDE,JetBrains的WebStorm中使用。

请参阅lib::relative的POD


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接