如何获取正在执行的Perl脚本的完整路径?


168

我有Perl脚本,需要在执行期间确定脚本的完整路径和文件名。我发现,这取决于你如何调用脚本$0而异,有时包含fullpath+filename有时刚filename。因为工作目录也可能变化,所以我想不出一种可靠地获取fullpath+filename脚本的方法。

任何人都有解决方案吗?

Answers:


251

有几种方法:

  • $0 是相对于当前工作目录的,由POSIX提供的当前正在执行的脚本(如果脚本位于CWD或以下)
  • 此外,cwd()getcwd()abs_path()由提供Cwd模块,并告诉你的脚本是从运行
  • 该模块FindBin提供$Bin$RealBin变量,通常是执行脚本的路径;该模块还提供$Script$RealScript,它们是脚本的名称
  • __FILE__ 是Perl解释器在编译过程中要处理的实际文件,包括其完整路径。

我已经看到前三个($0Cwd模块和FindBin模块)在mod_perl异常情况下失败,产生了毫无价值的输出,例如'.'或空字符串。在这样的环境中,我使用模块__FILE__并从中获取路径File::Basename

use File::Basename;
my $dirname = dirname(__FILE__);

2
这确实是最好的解决方案,尤其是如果您已经有修改后的$ 0
Caterham 2012年

8
看起来abs_path需要与_____FILE_____一起使用,因为它可能只包含路径名称。
余震

6
@vicTROLLA可能是因为此答案中的最大建议(将dirname与一起使用__FILE__)不能按预期工作?我最终得到了执行脚本的相对路径,而被接受的答案给出了完整的绝对路径。
2013年

10
dirname(__FILE__)不会跟随符号链接,因此,如果您链​​接了可执行文件,并且希望在安装位置中找到其他文件的位置,则需要检查if( -l __FILE__),然后单击dirname(readlink(__FILE__))
DavidG 2014年

3
@IliaRostovtsev您可以通过以下命令找到何时将模块首次包含在标准模块中:perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo。为此File::Basename,是90年代末发布的Perl 5.0.0,我认为现在已经可以节省使用。
德鲁·斯蒂芬斯

145

$ 0通常是程序的名称,那么这又如何呢?

use Cwd 'abs_path';
print abs_path($0);

在我看来,这应该作为abs_path知道您正在使用相对还是绝对路径。

更新对于这几年以后阅读的任何人,您都应该阅读Drew的答案。比我的好多了。


11
小注释,在Windows $ 0上的activestate perl中,通常包含反斜杠和abs_path返回的正斜杠,因此请快速输入“ tr / \ // \\ /;”。需要修复它。
克里斯·马登

3
想补充一点,如果您更喜欢使用不带下划线的名称realpath,那么它是的同义词abs_path
vol7ron 2012年

@Chris,您是否向Cwd模块维护人员报告了错误?似乎是Windows采用错误。
Znik 2014年

1
perl -e 'use Cwd "abs_path";print abs_path($0);'/tmp/-e
遇到的

2
@leonbloy当您内联执行脚本(使用-e)时,我相信perl会创建一个临时文件来存储您的内联脚本。就您而言,位置似乎是/tmp。您期望结果是什么?
GreenGiant


16

我认为您要查找的模块是FindBin:

#!/usr/bin/perl
use FindBin;

$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";

11

您可以使用FindBinCwdFile :: Basename或它们的组合。它们全部在Perl IIRC的基本发行版中。

我过去使用过Cwd:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";

@bmdhacks,您是对的。假设是,您没有更改0 $。例如,您在脚本启动后(在初始化块中)或在不更改$ 0的其他地方进行上述工作。但是$ 0是更改在'
ps'Unix

9

获得$0或到达的绝对路径__FILE__是您想要的。唯一的麻烦是,如果某人做了a chdir()并且the $0是相对的-那么您需要获取a中的绝对路径BEGIN{}以防止出现任何意外。

FindBin尝试更好地搜索和$PATH匹配的东西basename($0),但是有时候这样做太令人惊讶了(特别是:当文件在cwd中“就在您的面前”时)。

File::FuFile::Fu->program_name并且File::Fu->program_dir为此。


真的有人chdir()在编译时会愚蠢到(永久)吗?
SamB

只需根据脚本当前的目录和$ 0开始所有工作即可。
Znik

7

一些简短的背景:

不幸的是,Unix API没有为正在运行的程序提供可执行文件的完整路径。实际上,执行您的程序可以在通常告诉您程序是什么的字段中提供所需的内容。正如所有答案所指出的那样,有各种启发式方法可以找到可能的候选人。但是搜索整个文件系统总是可行的,即使移动或删除了可执行文件,即使这样也会失败。

但是您不希望Perl可执行文件(实际上正在运行),而是要执行的脚本。而且Perl需要知道脚本在哪里可以找到它。它将它存储在中__FILE__,而$0来自Unix API。这仍然可以是相对路径,因此请接受Mark的建议并通过File::Spec->rel2abs( __FILE__ );


__FILE__仍然给了我一条相对的道路。即“。”。
felwithe

6

你有没有尝试过:

$ENV{'SCRIPT_NAME'}

要么

use FindBin '$Bin';
print "The script is located in $Bin.\n";

这实际上取决于它的调用方式以及它是CGI还是从常规Shell运行等。


当脚本在控制台上运行时,$ ENV {'SCRIPT_NAME'}为空
Putnik 2014年

不好的主意,因为SCRIPT_NAME环境取决于您使用的shell。与Windows cmd.exe完全不兼容,而直接从其他二进制文件中调用脚本时则不兼容。没有保修条款。以上方法更加有用。
Znik

6

为了获取包含我的脚本的目录的路径,我使用了已经给出的答案的组合。

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;

my $dir = dirname(File::Spec->rel2abs(__FILE__));


2

无需使用外部模块,只需一行即可获得文件名和相对路径。如果您正在使用模块,并且需要应用相对于脚本目录的路径,则相对路径就足够了。

$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";

如果像“ ./myscript.pl”那样运行脚本,它不会提供正确的脚本完整路径,因为它只会显示“”。代替。但是我仍然喜欢这种解决方案。
凯夫

1
#!/usr/bin/perl -w
use strict;


my $path = $0;
$path =~ s/\.\///g;
if ($path =~ /\//){
  if ($path =~ /^\//){
    $path =~ /^((\/[^\/]+){1,}\/)[^\/]+$/;
    $path = $1;
    }
  else {
    $path =~ /^(([^\/]+\/){1,})[^\/]+$/;
    my $path_b = $1;
    my $path_a = `pwd`;
    chop($path_a);
    $path = $path_a."/".$path_b;
    }
  }
else{
  $path = `pwd`;
  chop($path);
  $path.="/";
  }
$path =~ s/\/\//\//g;



print "\n$path\n";

:DD


4
请不要只回答代码。请解释为什么这是正确的答案。
Lee Taylor

1

您在寻找这个吗?

my $thisfile = $1 if $0 =~
/\\([^\\]*)$|\/([^\/]*)$/;

print "You are running $thisfile
now.\n";

输出将如下所示:

You are running MyFileName.pl now.

它在Windows和Unix上均可使用。


0
use strict ; use warnings ; use Cwd 'abs_path';
    sub ResolveMyProductBaseDir { 

        # Start - Resolve the ProductBaseDir
        #resolve the run dir where this scripts is placed
        my $ScriptAbsolutPath = abs_path($0) ; 
        #debug print "\$ScriptAbsolutPath is $ScriptAbsolutPath \n" ;
        $ScriptAbsolutPath =~ m/^(.*)(\\|\/)(.*)\.([a-z]*)/; 
        $RunDir = $1 ; 
        #debug print "\$1 is $1 \n" ;
        #change the \'s to /'s if we are on Windows
        $RunDir =~s/\\/\//gi ; 
        my @DirParts = split ('/' , $RunDir) ; 
        for (my $count=0; $count < 4; $count++) {   pop @DirParts ;     }
        my $ProductBaseDir = join ( '/' , @DirParts ) ; 
        # Stop - Resolve the ProductBaseDir
        #debug print "ResolveMyProductBaseDir $ProductBaseDir is $ProductBaseDir \n" ; 
        return $ProductBaseDir ; 
    } #eof sub 

虽然仅源答案可能会解决用户的问题,但这并不能帮助他们理解其工作原理。您给了用户一条鱼,但是相反,您应该教他们如何钓鱼。
Tin Man

0

问题所在 __FILE__在于它将打印核心模块“ .pm”路径,而不必打印正在运行的“ .cgi”或“ .pl”脚本路径。我想这取决于您的目标是什么。

在我看来,Cwd只需要为mod_perl更新即可。这是我的建议:

my $path;

use File::Basename;
my $file = basename($ENV{SCRIPT_NAME});

if (exists $ENV{MOD_PERL} && ($ENV{MOD_PERL_API_VERSION} < 2)) {
  if ($^O =~/Win/) {
    $path = `echo %cd%`;
    chop $path;
    $path =~ s!\\!/!g;
    $path .= $ENV{SCRIPT_NAME};
  }
  else {
    $path = `pwd`;
    $path .= "/$file";
  }
  # add support for other operating systems
}
else {
  require Cwd;
  $path = Cwd::getcwd()."/$file";
}
print $path;

请添加任何建议。


0

没有任何对shell有效的外部模块,即使使用'../'也可以很好地工作:

my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";

测试:

$ /my/temp/Host$ perl ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ../Host/./host-mod.pl 
self=/my/temp/Host/host-mod.pl

调用symlink时会怎样?Cwd在这种情况下效果很好。
Znik 2014年

0

仅使用dirname(__FILE__)它的问题是它不遵循符号链接。我必须在脚本中使用它来跟随符号链接到达实际的文件位置。

use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
  $script_dir = dirname(readlink(__FILE__));
}
else {
  $script_dir = dirname(__FILE__);
}

0

实际上,所有无库解决方案都无法通过多种方式来编写路径(请考虑../或/bla/x/../bin/./x/../等。我的解决方案看起来像我有一个怪癖:我不知道为什么我必须两次运行替换项,否则我会得到一个虚假的“ ./”或“ ../”。对我来说似乎很健壮。

  my $callpath = $0;
  my $pwd = `pwd`; chomp($pwd);

  # if called relative -> add pwd in front
  if ($callpath !~ /^\//) { $callpath = $pwd."/".$callpath; }  

  # do the cleanup
  $callpath =~ s!^\./!!;                          # starts with ./ -> drop
  $callpath =~ s!/\./!/!g;                        # /./ -> /
  $callpath =~ s!/\./!/!g;                        # /./ -> /        (twice)

  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /
  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /   (twice)

  my $calldir = $callpath;
  $calldir =~ s/(.*)\/([^\/]+)/$1/;

0

“最佳”答案都不适合我。使用FindBin'$ Bin'或Cwd的问题是它们返回所有符号链接已解析的绝对路径。在我的情况下,我需要带有符号链接的确切路径-与返回Unix命令“ pwd”相同,而不是“ pwd -P”。以下功能提供了解决方案:

sub get_script_full_path {
    use File::Basename;
    use File::Spec;
    use Cwd qw(chdir cwd);
    my $curr_dir = cwd();
    chdir(dirname($0));
    my $dir = $ENV{PWD};
    chdir( $curr_dir);
    return File::Spec->catfile($dir, basename($0));
}

0

在Windows上,使用dirnameabs_path一起最适合我。

use File::Basename;
use Cwd qw(abs_path);

# absolute path of the directory containing the executing script
my $abs_dirname = dirname(abs_path($0));
print "\ndirname(abs_path(\$0)) -> $abs_dirname\n";

原因如下:

# this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__); 
print "dirname(__FILE__) -> $rel_dirname\n"; 

# this gives the slightly wrong answer, but in the form I want 
my $full_filepath = abs_path($0);
print "abs_path(\$0) -> $full_filepath\n";

-2

这有什么错$^X

#!/usr/bin/env perl<br>
print "This is executed by $^X\n";

将为您提供所用Perl二进制文件的完整路径。

翻转


1
它提供了指向Perl二进制文件的路径,同时提供了所需脚本的路径
Putnik 2014年

-5

在* nix上,您可能具有“ whereis”命令,该命令在$ PATH中搜索具有给定名称的二进制文件。如果$ 0不包含完整路径名,则运行whereis $ scriptname并将结果保存到变量中将告诉您脚本的位置。


那将行不通,因为$ 0还可能返回文件的相对路径:../perl/test.pl
Lathan

如果可执行脚本超出PATH会发生什么?
Znik 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.