在Perl中,我如何获取当前执行代码的目录或路径?

14

如果我在某个库代码中,如何确定当前执行的代码文件的路径?我知道通过查看ARGV可以获取顶层 Perl 文件的路径,但是如果我加载一个库,那么这个怎样知道它所在的路径呢?

5个回答

20

__FILE__ token可以给你包括文件名在内的完整路径。你可以使用File::Spec来将其分解成组件:

my ($volume, $directory, $file) = File::Spec->splitpath(__FILE__);

我认为这个程序无法正确处理相对路径。 - Leon Timmermans
@Leon:这取决于你所说的“正确”。__FILE__的值应该与%INC返回的相同。如果您已将相对路径添加到@INC中,则可能会返回相对文件路径。 splitpath()可以处理此问题,但目录部分将是相对而不是绝对的。如果这是一个问题,您可以使用File::Spec->rel2abs()将(可能)相对路径转换为绝对路径。 - Michael Carman
当然,__FILE__实际上可能并不是文件。在文件的某个地方输入#line 42 foo.bar,然后运行warn __FILE__来尝试一下。 - jrockway
4
有人真的尝试过这个吗?我只得到了脚本的名称,而不是完整路径。此外,我在互联网上搜索了“__FILE__”,所有我看到的文档都说这正是我应该得到的! - John Deighan
运行得非常顺畅。 - Géza Török
对于我而言,使用“Win32 strawberry-perl 5.30.2.1”,__FILE__可能并不总是返回完整路径,而是返回提供给perl二进制文件的脚本名称。因此,当通过“perl ./myscript.pl”启动时,__FILE__返回“./myscript.pl”... - Ted

15

使用FindBin是找到当前可执行文件的文件名最简便的方法:

use FindBin;
use File::Spec;

print "the directory of my script is: " . $FindBin::Bin . "\n";
print "the base name of my script is: " . $FindBin::Script . "\n";
print "the canonical location of my script is: " . File::Spec->catfile($FindBin::Bin, $FindBin::Script) . "\n";

可以通过查看$0(在命令行调用时脚本的名称)和__FILE__(当前执行文件的名称)来获取部分信息。

要提取当前执行模块的文件名,请从检查__PACKAGE__开始,进行一些替换操作后查找%INC中的文件名。详见perldoc perlvar

(my $filename = __PACKAGE__ ) =~ s#::#/#g;
$filename .= '.pm';
my $abs_filename = $INC{$filename};

我在我的初始化库中这样做,以便在相对于当前模块的路径中找到配置脚本(我安装了几个代码分支,并且每个分支都有略微不同的配置):

# use the location of the current module as a guide for where to find configs
(my $filename = __PACKAGE__ ) =~ s#::#/#g;
$filename .= '.pm';

(my $path = $INC{$filename}) =~ s#/\Q$filename\E$##g; # strip / and filename
my $abs_config_file = File::Spec->catfile($path, $config_file);
MyApp->initialize($abs_config_file);

这将为他提供从命令行执行的脚本的名称,而不是正在执行的“库模块”的名称(我认为)。 - lexu
@lexu:我在下一段落中添加了那一部分(就在你留下评论后面):) - Ether
没错,答案很好,但比 _ _ FILE _ _ 复杂一些。 - lexu
2
你提到了__FILE__和使用%INC的解决方案。有没有避免使用更简单的__FILE__变量,而改用%INC解决方案的原因? - Patrick Böker

1

通过userequire引入的任何库都会在特殊的%INC哈希表中产生一个条目(参见perlvar)。

例如:

use strict;
use warnings;

use Data::Dumper;

print Dumper \%INC;

这将会产生类似以下的输出结果:

$VAR1 = {
      'warnings/register.pm' => '/usr/lib/perl5/5.8.8/warnings/register.pm',
      'bytes.pm' => '/usr/lib/perl5/5.8.8/bytes.pm',
      'XSLoader.pm' => '/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/XSLoader.pm',
      'Carp.pm' => '/usr/lib/perl5/5.8.8/Carp.pm',
      'Exporter.pm' => '/usr/lib/perl5/5.8.8/Exporter.pm',
      'strict.pm' => '/usr/lib/perl5/5.8.8/strict.pm',
      'warnings.pm' => '/usr/lib/perl5/5.8.8/warnings.pm',
      'overload.pm' => '/usr/lib/perl5/5.8.8/overload.pm',
      'Data/Dumper.pm' => '/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/Data/Dumper.pm'
    };

此外,还有一个通用的__FILE__常量,它将返回当前文件名。(另请参见__PACKAGE__)。

1
这是我通常用来获取执行代码路径的片段。
use Cwd qw/abs_path/;
my ($real_path) = abs_path($0) =~ m/(.*)myscript.pl/i;

这将实际目录路径放入$real_path中。通常在此之后我还会执行chdir $real_path,以确保我的代码实际上是从应该的目录中运行的(通常在使用Win32::Daemon编写Windows服务时)。

我导出的abs_path子例程会给出您提供的任何文件(名称/句柄)的路径。在这种情况下,我提供了$0,它是正在执行的Perl脚本的名称。

我建议查看Cpan上的Cwd以获取更多指导。


稍微通用一些的写法: 使用 Cwd qw/abs_path/; ( my $script_path = abs_path($0) ) =~ s|^(.)([/\])(.)|$1$2|g ; - Yordan Georgiev

-4

最简单的方法:

print $ENV{'PWD'};

1
不幸的是,那样不起作用。 如果我从/home/luser执行/foo/bar/baz,则$ENV{PWD}将返回/home/luser/而不是/foo/bar。 - Ross Rogers
2
欢迎来到SO。你的答案可能是正确的,也可能不是,但无论如何,我们都鼓励人们解释他们的答案,而不仅仅是发布代码片段。 - Software Engineer
2
PWD是bash(可能是其他shell)env自动设置的变量。当某个进程更改目录并调用您的脚本时,PWD指向先前的目录,而不是当前的目录。最好删除此答案以防止失去分数 :) - Znik

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