一个Perl程序如何知道在哪里找到它使用的Perl模块所在的文件?

16
如果我的Perl程序使用了Perl模块,它将如何确定找到包含模块代码的文件?
例如,如果程序包含以下内容:
use MyModule1;              # Example 1
use This::Here::MyModule2;  # Example 2

它会搜索哪里?


我在SO上找不到一个全面的答案可以链接,所以我决定创建一个。如果下面提供的答案需要添加/更正,请随意 :) - DVK
3个回答

15
Perl解释器(运行您的perl程序的)将使用一个名为@INC的特殊数组来搜索包含模块的文件。 @INC数组中的每个值都是一个目录名称(但请参见下面的注释); Perl将按照指定的规则在这些目录中循环搜索。 (有关如何确定@INC内容的详细信息,请参阅此SO帖子)。
如果在耗尽@INC后未找到模块的文件,则程序的编译将中止并显示错误。 如果在@INC指定的目录之一中找到了模块的文件,则搜索将在不查看其余@INC的情况下结束。
Perl在列在@INC中的每个目录中搜索模块文件的方式如下:
首先,它将分离模块名称的层次组件(由 :: 分隔的单词),分为最后一个组件 - 用于形成文件名 - 和层次结构路径(前面的所有组件最后一个 :: )。如果模块名称仅有一个组件(没有 :: ,例如上面的 MyModule1 ),则层次结构路径为空,文件名是模块的名称。在这个问题的第二个例子中,最后一个组件是 MyModule2 ,层次结构路径将是 This :: Here 。
预期的文件名将通过将模块名称的最后一个组件附加到 .pm 扩展名来确定。例如,在我们的示例中, MyModule1.pm 和 MyModule2.pm 。
模块的目录将通过以下方式确定:
@INC 中获取下一个目录-以 / usr / lib / perl 作为示例
通过获取模块名称的层次结构路径(如果有)并将“ ::”替换为 / 或操作系统使用的任何字符作为目录分隔符来形成该目录的子目录。在我们的两个示例中,第一个模块将在 / usr / lib / perl (无子目录)中搜索,而第二个模块将在 / usr / lib / perl / This / Here 中搜索。
注意:模块名称在Unix和其他操作系统上显然区分大小写,其中文件/目录命名区分大小写。@INC 还可能包含子例程引用和对象引用,它们根据其自定义代码指定的方式加载模块,而不是按照#2逻辑中指定的目录执行查找。该功能似乎很少使用,并且本文假定整个 @INC 仅包含目录。
让我们看一个具体的例子,假设你的@INC包含两个子目录:("/usr/lib/perl", "/opt/custom/lib")
那么Perl将按以下方式搜索:
========================================================================== | 模块 | 尝试次数 | 尝试的文件 ========================================================================== | MyModule1 | 尝试 1 | /usr/lib/perl/MyModule1.pm | MyModule1 | 尝试 2 | /opt/custom/lib/MyModule1.pm ========================================================================== | This::Here::MyModule2 | 尝试 1 | /usr/lib/perl/This/Here/MyModule2.pm | This::Here::MyModule2 | 尝试 2 | /opt/custom/lib/This/Here/MyModule2.pm ==========================================================================
请注意,一旦Perl在其中一个位置找到文件,它就会停止尝试搜索,而不会尝试查看文件是否在后续位置中存在。例如,如果/usr/lib/perl/This/Here/MyModule2.pm存在,则Perl将不会查找也不关心/opt/custom/lib/This/Here/MyModule2.pm的存在。
注意:每当Perl解释器使用类似于require的机制导入Perl模块时,都会使用@INC。这包括:
  • require指令本身
  • use MyModule语句(相当于require+import)
  • use base(相当于require+"push @ISA")
  • -M命令行参数

2
值得注意的是,@INC 的内容来自哪里。这甚至可能是 OP 寻找的答案。简要概述:主要默认内容是内置的(路径的确切细节取决于您的安装)。在脚本之外修改它的主要方法是设置环境变量 PERL5LIB(一个由冒号分隔的路径列表)或在运行时向可执行文件提供“-I/path/to/dir”选项。(这些选项将被添加到数组前面) - Cascabel
有没有一个权威的、精准的 @INC 构建列表?perldoc perlvar 中的列表貌似没有提到 PERL5LIB,以及 $Config{sitelib}/sitecustomize.pl 机制(必须在编译时构建)。 - Cascabel
@DVK:太棒了!由于对问题的评论和这已经非常详尽,我错误地认为你已经完成了。 - Cascabel
您能将此标记为 CW,以便低声望用户也可以编辑他们的答案吗? - Ether
我还添加了一个关于在@INC中具有子程序引用和对象的能力的参考。 - DVK
显示剩余2条评论

8
虽然这并没有直接回答问题,但是以下是一些简单的技巧来确定你想要使用的模块文件的完整路径。
要查看@INC数组的默认内容以及其他大量信息,请从命令行运行。
perl -V      

如果你想知道Carp模块的位置:
perldoc -l Carp

在脚本中,打印%INC哈希表的内容非常有用,可以确定实际使用的模块,特别是如果您已经修改了@INC的默认值。
use Carp;
print $INC{'Carp.pm'};

这个简单的脚本也可以用于查找符合正则表达式的已安装Perl模块,并识别不同目录中的任何重复模块。


@toolic - 这个答案与 OP 的问题密切相关,但我觉得它有些独立(例如,“我导入的模块来自哪里”)。你介意把它作为一个单独的 SO 问答发布(我会链接到它),或者给我你的同意让我提出一个单独的问题并发布你的答案(或让我重新发布你的答案)吗? - DVK
如何查找包含Perl模块的文件? - DVK
如果我在Windows的环境变量路径中有两个Perl版本,那么系统如何知道选择哪个Perl?是第一个吗?我的机器上安装了不同的软件,每个软件都有不同的Perl。它们是随着软件一起安装的。 - stack1

3
根据perlfunc有关use的文档

use Module LIST

Imports some semantics into the current package from the named module, generally by aliasing certain subroutine or variable names into your package. It is exactly equivalent to

BEGIN { require Module; Module->import( LIST ); }

except that Module must be a bareword.

所以require承担了繁重的工作,而require文档提供了帮助。

If EXPR is a bareword, the require assumes a ".pm" extension and replaces "::" with "/" in the filename for you, to make it easy to load standard modules. This form of loading of modules does not risk altering your namespace.

In other words, if you try this:

   require Foo::Bar;    # a splendid bareword

The require function will actually look for the "Foo/Bar.pm" file in the directories specified in the @INC array.


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