Perl 程序如何知道在哪里可以找到包含它使用的 Perl 模块的文件?

Posted

技术标签:

【中文标题】Perl 程序如何知道在哪里可以找到包含它使用的 Perl 模块的文件?【英文标题】:How does a Perl program know where to find the file containing Perl module it uses? 【发布时间】:2011-02-01 08:41:00 【问题描述】:

如果我的 Perl 程序使用 Perl 模块,它将如何确定在哪里可以找到包含模块代码的文件?

例如,如果程序包含:

use MyModule1;              # Example 1
use This::Here::MyModule2;  # Example 2

它会在哪里看?

【问题讨论】:

我在 SO 上找不到可以链接到的这个问题的全面答案,所以我决定创建一个。如果下面提供的答案需要补充/更正,请采纳:) 【参考方案1】:

Perl 解释器(运行您的 perl 程序)将使用一个名为 @INC 的特殊数组来搜索包含该模块的文件。

@INC 数组中的每个值都是一个目录名称(但请参阅下面的注释); Perl 将使用下面指定的规则在循环中搜索这些目录。 (请参考this SO post for details of how the contents of @INC are determined)。

如果在耗尽@INC 后没有找到模块的文件,程序的编译将被错误中止。如果在@INC 中指定的目录之一中找到模块文件,则搜索结束,无需查看@INC 的其余部分。

Perl 在@INC 中列出的每个目录中搜索模块文件的方式如下:

首先,它将模块名称的分层组件(由::分隔的单词)分成最后一个组件 - 将用于形成文件名 - 和层次结构路径(最后一个@987654331之前的所有组件@)。

如果模块名称只有一个组件(没有::,例如上面的MyModule1),层次结构路径为空,文件名是模块的名称。在这个问题的第二个例子中,最后一个组件是MyModule2,层次结构路径是This::Here

预期的文件名将通过在模块名称的最后一个组成部分附加.pm 扩展名来确定。例如。 MyModule1.pmMyModule2.pm 在我们的示例中。

注意:在 Unix 和其他文件/目录命名区分大小写的操作系统上,模块名称显然区分大小写。

模块的目录将由以下因素决定:

    @INC 获取下一个目录 - 以/usr/lib/perl 为例

    通过获取模块名称(如果有)的层次结构路径并将“::”替换为/ 或操作系统用作目录分隔符的任何字符来形成该目录的子目录。在我们的两个示例中,将在 /usr/lib/perl(无子目录)中搜索第一个模块,在 /usr/lib/perl/This/Here 中搜索第二个模块。

    注意:上面是一个轻微的简化 - @INC may also contain subroutine references and object references,它按照自定义代码指定的方式加载模块,而不是在 # 中指定的目录中执行查找2 上面的逻辑。该功能似乎很少使用,本文假设整个 @INC 仅包含目录。

我们来看一个具体的例子,假设你的@INC 包含两个子目录: ("/usr/lib/perl", "/opt/custom/lib")

然后 Perl 会搜索如下:

==================================================== ========================= |模块 |试试# |文件尝试 ==================================================== ========================= |我的模块1 |尝试 1 | /usr/lib/perl/MyModule1.pm |我的模块1 |尝试 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命令行参数

【讨论】:

可能值得注意的是@INC 的内容来自哪里。这甚至可能是 OP 正在寻找的答案。简要总结:主要的默认内容是内置的(路径的确切细节显然取决于您的安装)。在脚本之外修改它的主要方法是设置环境变量 PERL5LIB(以冒号分隔的路径列表)或在运行时为可执行文件提供 -I/path/to/dir 选项。 (这些添加到数组中) 任何人都有一份关于@INC 是如何构造的权威列表? perldoc perlvar 中的那个似乎没有提及 PERL5LIB,以及 $Configsitelib/sitecustomize.pl 机制(必须在编译时内置)。 @DVK:太好了!错误地认为你已经完成了,因为对问题的评论以及这已经非常彻底的事实。 您能否标记此 CW,以便低代表用户也可以编辑他们的答案? @Ether - 完成。我的代表雪崩了:(;)【参考方案2】:

虽然这并不能直接回答问题,但这里有一些简单的技术可以确定您要使用的模块文件的完整路径。

从命令行查看@INC 数组的默认内容以及许多其他信息:

perl -V      

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

perldoc -l Carp

在脚本中,打印 %INC 哈希的内容对于确定您正在使用的实际模块很有用,特别是如果您修改了 @INC 的默认值:

use Carp;
print $INC'Carp.pm';

这个简单的脚本也可用于Find installed Perl modules matching a regular expression 并识别不同目录中的任何重复模块。

【讨论】:

@toolic - 这个答案与 OP 问题密切相关,但我觉得它与它有些不同(例如“我导入的模块来自哪里”)。您介意在 SO 上将其作为单独的 Q+A 发布(我将链接到它),还是同意我将其作为单独的 Q 提问并发布您的答案(或让我重新发布您的答案)? 完成! ***.com/questions/2527990/… @DVK - 如果我的 Windows 路径环境变量中有两个版本的 perl,那么系统如何知道选择哪个 perl?是第一个吗?对于我的机器上安装的不同软件,我有不同的 perls。它们与软件一起安装。【参考方案3】:

根据perlfunc documentation on use

使用模块列表

将一些语义从命名模块导入当前包,通常通过将某些子例程或变量名称别名到包中。完全等价于

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

除了 Module 必须是一个裸词。

所以require 完成了繁重的工作,而require documentation 提供了

如果 EXPR 是一个裸字,则 require 假定为 ".pm" 扩展名,并将文件名中的 "::" 替换为 "/",以便于加载标准模块。这种形式的模块加载不会改变您的命名空间。

换句话说,如果你试试这个:

   require Foo::Bar;    # a splendid bareword

require 函数实际上会在@INC 数组中指定的目录中查找"Foo/Bar.pm" 文件。

【讨论】:

以上是关于Perl 程序如何知道在哪里可以找到包含它使用的 Perl 模块的文件?的主要内容,如果未能解决你的问题,请参考以下文章

在哪里可以找到 MySQL 事务日志?

Perl 杂志可以在线获得吗? [关闭]

如何在现代 Linux 上的 Perl 中运行程序(超时)并知道它是如何结束的(信号、退出代码等)?

如何判断头文件包含在哪里?

如果我知道其中的标签,如何使用 Perl 的 HTML 模块找到 div 的内容?

我在哪里可以找到 couchdb 应用程序的列表?