it-swarm.cn

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

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

有人有解决方案吗?

158
Chris Madden

有几种方法:

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

我看到前三个( $0Cwd module和 FindBin module)在mod_Perl下非常失败,产生了无价值的输出,如'.'或空字符串。在这样的环境中,我使用 __FILE__ 并使用 File::Basename module获取路径:

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

$ 0通常是您的程序的名称,那么这个怎么样?

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

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

更新 对于今年读书的人,你应该阅读以下Drew的答案。它比我好多了。

143
Ovid
Use File::Spec;
File::Spec->rel2abs( __FILE__ );

http://perldoc.Perl.org/File/Spec/Unix.html

34
Mark

我认为您正在寻找的模块是FindBin:

#!/usr/bin/Perl
use FindBin;

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

您可以使用 FindBinCwdFile :: Basename 或它们的组合。它们都在Perl IIRC的基础版本中。

我过去使用过Cwd:

CWD:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
11
Benjamin W. Smith

获得$0__FILE__的绝对路径是你想要的。唯一的麻烦是如果有人做chdir()$0是相对的 - 那么你需要在BEGIN{}中获得绝对路径以防止任何意外。

FindBin尝试更好地在$PATH中找到与basename($0)匹配的东西,但有时候会发生太令人惊讶的事情(特别是:当cwd中的文件“在你面前”时)。

为此,File::FuFile::Fu->program_nameFile::Fu->program_dir

9
Eric Wilhelm

一些简短的背景:

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

但是你不需要Perl可执行文件,它是实际运行的,但是它正在执行的脚本。 Perl需要知道脚本在哪里找到它。它将它存储在__FILE__中,而$0来自Unix API。这仍然是一个相对的路径,所以采取马克的建议,并用File::Spec->rel2abs( __FILE__ );封装它

7
wnoise

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

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

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

你有没有尝试过:

$ENV{'SCRIPT_NAME'}

要么

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

这实际上取决于它是如何被调用的,如果它是CGI或从普通的Shell运行,等等。

6
Sean

perlfaq8 使用$0上的rel2abs()函数回答了一个非常类似的问题。该函数可以在File :: Spec中找到。

2
moritz

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

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

你在找这个吗?:

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

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

输出将如下所示:

You are running MyFileName.pl now.

它适用于Windows和Unix。

1
Yong Li
#!/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

1
mkc
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 
0
Yordan Georgiev

没有一个“顶级”答案适合我。使用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
drjumper

仅使用dirname(__FILE__)的问题在于它不遵循符号链接。我不得不使用它来为我的脚本按照符号链接到实际的文件位置。

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

__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
Jonathan

没有任何外部模块,对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
0
Putnik

所有无库的解决方案实际上并不适用于编写路径的几种方法(想想../或/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
Elmar