Perl 的@INC 是如何构造的? (又名影响 Perl 模块搜索位置的所有方法是啥?)

Posted

技术标签:

【中文标题】Perl 的@INC 是如何构造的? (又名影响 Perl 模块搜索位置的所有方法是啥?)【英文标题】:How is Perl's @INC constructed? (aka What are all the ways of affecting where Perl modules are searched for?)Perl 的@INC 是如何构造的? (又名影响 Perl 模块搜索位置的所有方法是什么?) 【发布时间】:2011-02-01 09:05:08 【问题描述】:

影响 Perl 模块搜索位置的所有方式有哪些? 或者,Perl 的@INC 是如何构造的

我们知道,Perl uses @INC array containing directory names to determine where to search for Perl module files。

在 *** 上似乎没有全面的“@INC”常见问题解答类型的帖子,所以这个问题旨在作为一个问题。

【问题讨论】:

是的,但是search.cpan.org/perldoc/… 有一个非常好的? @mobrule:我认为这还不够全面——它只是说明如何在运行时添加到@INC,而不是完整的构造。 @mobrule - @Jefromi 猜对了 - 到目前为止,我发现的 ANY 和所有参考资料的主要问题是缺乏关于 perl 二进制文件的编译默认 @INC 的全面信息 有些人可以通过向我发送补丁来使这个答案更加全面。这很容易,因为 perlfaq 在 Github 中。 :) 嗯,“关于 perl 二进制编译的默认 @INC 的综合信息”是编译它的人设置的。如果您询问进入那里的各种路径,那么我认为您的问题与您回答的问题不同。 【参考方案1】:

我们将看看这个数组的内容是如何构造的,并且可以被操纵来影响 Perl 解释器在哪里找到模块文件。

    默认@INC

    Perl 解释器是compiled with a specific @INC default value。要找出此值,请运行 env -i perl -V 命令(env -i 忽略 PERL5LIB 环境变量 - 请参阅 #2),在输出中您将看到如下内容:

    $ env -i perl -V
    ...
    @INC:
     /usr/lib/perl5/site_perl/5.18.0/x86_64-linux-thread-multi-ld
     /usr/lib/perl5/site_perl/5.18.0
     /usr/lib/perl5/5.18.0/x86_64-linux-thread-multi-ld
     /usr/lib/perl5/5.18.0
     .
    

在末尾注明.;这是当前目录(不一定与脚本的目录相同)。它在 Perl 5.26+ 中缺失,当 Perl 使用 -T (taint checks enabled) 运行时。

配置Perl二进制编译时要更改默认路径,设置配置选项otherlibdirs

Configure -Dotherlibdirs=/usr/lib/perl5/site_perl/5.16.3

    环境变量PERL5LIB(或PERLLIB

    Perl 在@INC 前面加上一个包含在PERL5LIB(如果未定义,则使用PERLLIB)环境变量中的目录列表(以冒号分隔)。要在PERL5LIBPERLLIB环境变量生效后查看@INC的内容,运行perl -V

    $ perl -V
    ...
    %ENV:
      PERL5LIB="/home/myuser/test"
    @INC:
     /home/myuser/test
     /usr/lib/perl5/site_perl/5.18.0/x86_64-linux-thread-multi-ld
     /usr/lib/perl5/site_perl/5.18.0
     /usr/lib/perl5/5.18.0/x86_64-linux-thread-multi-ld
     /usr/lib/perl5/5.18.0
     .
    

    -I 命令行选项

    Perl 在@INC 前面加上一个目录列表(以冒号分隔)作为-I 命令行选项的值传递。这可以通过三种方式完成,与 Perl 选项一样:

    在命令行传递它:

    perl -I /my/moduledir your_script.pl
    

    通过 Perl 脚本的第一行 (shebang) 传递它:

    #!/usr/local/bin/perl -w -I /my/moduledir
    

    将其作为PERL5OPT(或PERLOPT)环境变量的一部分传递(参见Programming Perl 中的第19.02 章)

    通过lib pragma传递它

    Perl 在@INC 前面加上一个通过use lib 传递给它的目录列表。

    在程序中:

    use lib ("/dir1", "/dir2");
    

    在命令行上:

    perl -Mlib=/dir1,/dir2
    

    你也可以remove the directories from @INC via no lib

    您可以直接将@INC 操作为常规 Perl 数组。

    注意:由于在编译阶段使用了@INC,因此必须在BEGIN 块内完成,该块位于use MyModule 语句之前。

    通过unshift @INC, $dir将目录添加到开头。

    通过push @INC, $dir将目录添加到末尾。

    用 Perl 数组做任何你能做的事情。

注意:目录按此答案中列出的顺序未移动@INC,例如默认@INC 在列表中最后一个,前面是PERL5LIB,前面是-I,前面是use lib 和直接@INC 操作,后两者在Perl 代码中以任意顺序混合。

参考资料:

perldoc perlmod perldoc lib Perl Module Mechanics - a great guide containing practical HOW-TOs How do I 'use' a Perl module in a directory not in @INC? Programming Perl - 第 31 章第 13 部分,第 7.2.41 章 How does a Perl program know where to find the file containing Perl module it uses?

在 Stack Overflow 上似乎没有一个全面的 @INC 常见问题解答类型的帖子,所以这个问题打算作为一个。

何时使用每种方法?

如果一个目录中的模块需要被站点上的许多/所有脚本使用,尤其是由多个用户运行,则该目录应该包含在编译到 Perl 二进制文件中的默认 @INC 中。

如果目录中的模块将由特定用户专门用于用户运行的所有脚本(或者如果重新编译 Perl 不是更改先前用例中默认 @INC 的选项),则设置用户的 @ 987654386@,通常在用户登录时。

注意:请注意常见的 Unix 环境变量陷阱 - 例如在某些情况下,以特定用户身份运行脚本并不能保证在该用户的环境设置下运行它们,例如通过su

如果目录中的模块只需要在特定情况下使用(例如在开发/调试模式下执行脚本时,您可以手动设置PERL5LIB,也可以通过@987654389 @perl 的选项。

如果模块需要仅用于特定脚本,所有用户使用它们,请在程序本身中使用use lib/no lib pragma。当需要在运行时动态确定要搜索的目录时,也应该使用它 - 例如。来自脚本的命令行参数或脚本的路径(请参阅FindBin 模块了解非常好的用例)。

如果@INC中的目录需要根据一些复杂的逻辑进行操作,要么通过use lib/no lib pragmas的组合来实现太笨重,那么直接在BEGIN 里面使用@INC操作块或在指定用于@INC 操作的专用库中,在使用任何其他模块之前,您的脚本必须使用该库。

这方面的一个示例是在 prod/uat/dev 目录中的库之间自动切换,如果 dev 和/或 UAT 中缺少瀑布库,则在 prod 中拾取瀑布库(最后一个条件使标准的“使用 lib + FindBin”解决方案相当复杂的。 这个场景的详细说明在How do I use beta Perl modules from beta Perl scripts?

直接操作@INC 的另一个用例是能够添加子例程引用或对象引用(是的,Virginia,@INC 可以包含自定义 Perl 代码而不仅仅是目录名称,如 @ 中所述987654335@)。

【讨论】:

别忘了PERLOPT,你可以在其中设置-I。此外,诸如 base.pm 和 local::lib 之类的内容会使用您隐式列出的内容。 @brian - use::base 使用它是因为 IIRC 下面的“require”。我不熟悉 local::lib,需要阅读更多内容才能了解它的含义 另外,为了让这个答案非常好,你需要告诉人们什么时候应该使用每一个。仅仅给他们 10 个选项来告诉他们如何做是没有多大帮助的。 :) P.S.任何人,请随时编辑答案以通过 -I/use lib 包含第二个特定于体系结构的目录。我计划稍后自己这样做,但现在需要离线。 @brian - 添加了 PERLOPT。我觉得提到 base.pm 和其他“何时使用 @INC”项目更适合我从这里发布和链接的 @INC/finding 模块文件常见问题解答,所以我把它放在那里。【参考方案2】:

除了上面列出的位置之外,OS X 版本的 Perl 还有另外两种方式:

    /Library/Perl/x.xx/AppendToPath 文件。此文件中列出的路径会在运行时附加到 @INC。

    /Library/Perl/x.xx/PrependToPath 文件。此文件中列出的路径会在运行时添加到 @INC。

【讨论】:

在运行时具体何时读取这些文件?当我查看 lsof 的输出时,我遇到了一种罕见的情况,即 AppendToPath 文件打开以在 fd 0 (STDIN) 上读取,但在我测试过的所有其他情况下,它都不存在,我似乎无法制作一个复制该行为的玩具示例。我很好奇它为什么以及何时出现在 STDIN 上。什么时候到? 我根据 lsof 研究了为什么我打开了那个句柄。显然,在调用显示打开句柄的脚本的脚本顶部,有一行代码正在关闭 STDIN(这可能是处理某些过去问题的错误尝试)。但显然,如果您这样做,处理的子脚本将显示 STDIN 在 macOS 上对该文件打开。不过还不知道为什么。【参考方案3】:

正如前面所说,@INC 是一个数组,你可以随意添加任何你想要的东西。

我的 CGI REST 脚本如下所示:

#!/usr/bin/perl
use strict;
use warnings;
BEGIN 
    push @INC, 'fully_qualified_path_to_module_wiht_our_REST.pm';

use Modules::Rest;
gone(@_);

子程序消失由 Rest.pm 导出。

【讨论】:

以上是关于Perl 的@INC 是如何构造的? (又名影响 Perl 模块搜索位置的所有方法是啥?)的主要内容,如果未能解决你的问题,请参考以下文章

(perl) 终端 (Mac) 如何在 @INC 中找到 LWP...?

Perl - 如何查看Perl模块路径

[Perl] 模块搜索路径变量 @INC

如何包含位于不同目录中的 Perl 模块?

Perl:将当前目录包含到@INC吗?

默认情况下,Perl 不包含 @INC 中的当前目录吗?