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

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

有人有办法吗?

200791 次浏览

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

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

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

在我看来,这应该作为 abs _ path 知道您使用的是相对路径还是绝对路径。

更新 对于今年以后阅读这篇文章的人来说,你应该读读 德鲁的回答,它比我的好多了。

你有没有试过:

$ENV{'SCRIPT_NAME'}

或者

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

这实际上取决于它是如何被调用的,以及它是 CGI 还是从一个普通的 shell 运行,等等。

我想你要找的模块是 FindBin:

#!/usr/bin/perl
use FindBin;


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

您可以使用 FindBinCWD文件: : Basename或它们的组合。他们都在 Perl IIRC 的基地。

我过去用过 Cwd:

运输署:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
use File::Spec;
File::Spec->rel2abs( __FILE__ );

Http://perldoc.perl.org/file/spec/unix.html

Perlfaq8 使用 $0上的 rel2abs()函数回答了一个非常类似的问题。

一些简短的背景:

不幸的是,UnixAPI 没有提供一个运行中的程序,其中包含可执行文件的完整路径。实际上,执行您的程序可以在通常告诉程序它是什么的字段中提供它想要的任何内容。正如所有的答案指出的那样,有各种各样的启发式方法来寻找可能的候选人。但是除了搜索整个文件系统之外,没有什么能够始终工作,如果可执行文件被移动或删除,即使是搜索也会失败。

但是您不需要 Perl 可执行文件,而 Perl 可执行文件是实际运行的,但是您需要它正在执行的脚本。Perl 需要知道脚本在哪里才能找到它。它将其存储在 __FILE__中,而 $0来自 UnixAPI。这仍然可以是一个相对的路径,所以采纳 Mark 的建议,并将其与 File::Spec->rel2abs( __FILE__ );一起推崇

有几种方法:

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

我已经看到前三个($0Cwd模块和 FindBin模块)在 mod_perl下出现了引人注目的故障,产生了毫无价值的输出,如 '.'或一个空字符串。在这样的环境中,我使用 Cwd0并使用 Cwd1模块从中获取路径:

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

获取到 $0__FILE__的绝对路径是您想要的。唯一的问题是,如果有人做了一个 chdir()$0是相对的-那么您需要得到一个 BEGIN{}的绝对路径,以防止任何意外。

FindBin试图做得更好,在 $PATH中寻找与 basename($0)相匹配的东西,但是有时候这会做出一些令人惊讶的事情(特别是: 当文件在 cwd 中“就在你面前”的时候)

File::FuFile::Fu->program_nameFile::Fu->program_dir

你在找这个吗:

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


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

输出如下:

You are running MyFileName.pl now.

它可以在 Windows 和 Unix 上运行。

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
#!/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

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

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


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

$^X怎么了?

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

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

埃弗特

__FILE__的问题在于它会打印核心模块”。下午“路径不一定”。“或”。正在运行的脚本路径。我想这取决于你的目标是什么。

在我看来,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;

请添加任何建议。

没有任何对 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

只使用 dirname(__FILE__)的问题在于它不遵循符号链接。我必须使用这个脚本来跟踪符号链接到实际的文件位置。

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

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

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

所有不使用库的解决方案实际上并不适用于编写路径的多种方法(请考虑。.或者不知所云。./垃圾桶/。/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/;

没有一个“最好”的答案适合我。使用 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));
}

在 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";