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

我有Perl脚本,需要在执行过程中确定脚本的完整path和文件名。 我发现,取决于你如何调用$0的脚本变化,有时包含fullpath+filename ,有时只是filename 。 因为工作目录可以改变,所以我想不出一种方法来可靠地获取脚本的fullpath+filename

任何人都有解决办法?

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

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

在我看来,这应该作为abs_path知道你是否使用相对或绝对path。

更新对于任何人阅读今年以后,你应该阅读下面的德鲁的答案。 这比我的好多了。

有几种方法:

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

我已经看到头三( $0Cwd模块和FindBin模块)在mod_perl下失败,产生无价值的输出,如'.' 或一个空string。 在这样的环境中,我使用__FILE__并使用File::Basename模块获取path:

 use File::Basename; my $dirname = dirname(__FILE__); 
 Use File::Spec; File::Spec->rel2abs( __FILE__ ); 

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

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

 #!/usr/bin/perl use FindBin; $0 = "stealth"; print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n"; 

你可以使用FindBin , Cwd , File :: Basename或者它们的组合。 他们都在Perl IIRC的基础版本中。

我以前用过Cwd:

CWD:

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

获取绝对path$0__FILE__是你想要的。 唯一的麻烦是,如果有人做了一个chdir()$0是相对的 – 那么你需要在BEGIN{}获取绝对path,以防止出现任何意外。

FindBin试图更好地在$PATH寻找匹配basename($0) ,但是有时候这种做法太令人吃惊了(具体来说:当文件在“正前方”时顺时针)。

File::Fu具有File::Fu->program_nameFile::Fu->program_dir

你有没有尝试过:

 $ENV{'SCRIPT_NAME'} 

要么

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

这真的取决于它是如何被调用,如果它是CGI或从一个正常的shell运行,等等。

一些简短的背景:

不幸的是,Unix API并没有提供一个正在运行的程序和可执行文件的完整path。 事实上,执行你的程序可以在这个领域提供任何想要的东西,这通常会告诉你的程序它是什么。 正如所有答案所指出的那样,为了find可能的候选人,有各种启发式的方法。 但是,search整个文件系统的工作总是无效的,如果可执行文件被移动或删除,甚至会失败。

但是,您不需要执行Perl的可执行文件,而是执行实际运行的脚本。 Perl需要知道脚本在哪里find它。 它存储在__FILE__ ,而$0来自Unix API。 这仍然是一个相对path,所以采取马克的build议,并使用File::Spec->rel2abs( __FILE__ );

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

 #!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Basename; my $dir = dirname(File::Spec->rel2abs(__FILE__)); 

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

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

 $0 =~ m/(.+)[\/\\](.+)$/; print "full path: $1, file name: $2\n"; 
 #!/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

你在找这个吗?

 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/^(.*)(\\|\/)(.*)\.([az]*)/; $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 

__FILE__的问题是它将打印核心模块“.pm”path不一定是正在运行的“.cgi”或“.pl”脚本path。 我想这取决于你的目标是什么。

在我看来, Cwd只需要更新mod_perl。 这是我的build议:

 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; 

请添加任何build议。

没有任何外部模块,对shell有效,即使使用“../”也能正常工作:

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

testing:

 $ /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__); } 

所有库免费的解决scheme实际上并没有超过几种方式来写一个path(想想../或/bla/x/../bin/./x/../等等。我的解决scheme看起来像我有一个怪癖:我不知道为什么我必须两次运行replace,如果我不这样做,我会得到一个伪造的“./”或“../”,除此之外,对我来说似乎相当强劲。

  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/; 

$^X什么问题?

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

会给你正在使用的Perl二进制文件的完整path。

翻转

在* nix上,您可能有“whereis”命令,它会search您的$ PATH,查找具有给定名称的二进制文件。 如果$ 0不包含完整的path名,那么运行whereis $ scriptname并将结果保存到variables中会告诉您脚本的位置。