我有Perl脚本,需要在执行期间确定脚本的完整路径和文件名。 我发现,取决于脚本的调用方式,$0有所不同,有时包含fullpath+filename,有时仅包含filename。 因为工作目录也可能变化,所以我想不出一种可靠地获取脚本fullpath+filename的方法。
任何人都有解决方案吗?
有几种方法:
-
$0是POSIX提供的当前正在执行的脚本,相对于当前工作目录(如果脚本位于CWD或以下)
-
此外,Cwd模块提供了cwd(),getcwd()和abs_path(),并告诉您脚本从何处运行
-
模块FindBin提供$Bin和$RealBin变量,它们通常是执行脚本的路径。此模块还提供$Script和$RealScript,它们是脚本的名称
-
__FILE__是Perl解释器在编译过程中要处理的实际文件,包括其完整路径。
我已经看到前三个($0,Cwd模块和FindBin模块)在mod_perl下严重失败,产生了毫无价值的输出,例如'.'或空字符串。在这样的环境中,我使用__FILE__并使用File::Basename模块从中获取路径:
1 2
| use File::Basename;
my $dirname = dirname(__FILE__); |
$ 0通常是程序的名称,那么这又如何呢?
1 2
| use Cwd 'abs_path';
print abs_path($0); |
在我看来,这应该作为abs_path知道您正在使用相对还是绝对路径。
更新对于几年后阅读的任何人,您都应该阅读Drew的答案。比我的好多了。
1 2
| Use File::Spec;
File::Spec->rel2abs( __FILE__ ); |
http://perldoc.perl.org/File/Spec/Unix.html
我认为您要查找的模块是FindBin:
1 2 3 4 5 6
| #!/usr/bin/perl
use FindBin;
$0 ="stealth";
print"The actual path to this is: $FindBin::Bin/$FindBin::Script\
"; |
您可以使用FindBin,Cwd,File :: Basename或它们的组合。它们全部在Perl IIRC的基本发行版中。
我过去使用过Cwd:
Cwd:
1 2 3 4
| use Cwd qw(abs_path);
my $path = abs_path($0);
print"$path\
"; |
您想要获得$0或__FILE__的绝对路径。唯一的麻烦是,如果有人做了chdir()并且$0是相对的-那么您需要在BEGIN{}中获取绝对路径,以防止出现任何意外情况。
FindBin试图做得更好,并在$PATH中徘徊,寻找与basename($0)相匹配的内容,但是有时候这会做得太令人惊讶了(特别是:当文件"就在您的面前"时)在cwd中。)
File::Fu为此具有File::Fu->program_name和File::Fu->program_dir。
一些简短的背景:
不幸的是,Unix API没有为运行的程序提供可执行文件的完整路径。实际上,执行您的程序可以在通常告诉您程序内容的字段中提供所需的内容。正如所有答案所指出的那样,有各种启发式方法可以找到可能的候选人。但是搜索整个文件系统总是可行的,即使移动或删除了可执行文件,即使这样也会失败。
但是您不希望Perl可执行文件(实际上正在运行),而是要执行的脚本。而且Perl需要知道脚本在哪里可以找到它。它存储在__FILE__中,而$0来自Unix API。这仍然可以是相对路径,因此请接受Mark的建议并使用File::Spec->rel2abs( __FILE__ );将其标准化
你有没有尝试过:
要么
1 2 3
| use FindBin '$Bin';
print"The script is located in $Bin.\
"; |
它实际上取决于如何调用它,以及它是CGI还是从普通shell运行等。
为了获得包含我的脚本的目录的路径,我使用了已经给出的答案的组合。
1 2 3 4 5 6 7
| #!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;
my $dir = dirname(File::Spec->rel2abs(__FILE__)); |
无需使用外部模块,只需一行即可获得文件名和相对路径。如果您正在使用模块,并且需要应用相对于脚本目录的路径,则相对路径就足够了。
1 2 3
| $0 =~ m/(.+)[\\/\\\\](.+)$/;
print"full path: $1, file name: $2\
"; |
perlfaq8使用$0上的rel2abs()函数回答了一个非常相似的问题。该功能可以在File :: Spec中找到。
您在寻找这个吗?
1 2 3 4 5 6
| my $thisfile = $1 if $0 =~
/\\\\([^\\\\]*)$|\\/([^\\/]*)$/;
print"You are running $thisfile
now.\
"; |
输出将如下所示:
1
| You are running MyFileName.pl now. |
它在Windows和Unix上均可使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #!/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"\
$path\
"; |
:DD
在Windows上,同时使用dirname和abs_path对我来说效果最好。
1 2 3 4 5 6 7 8
| 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"\
dirname(abs_path(\\$0)) -> $abs_dirname\
"; |
原因如下:
1 2 3 4 5 6 7 8 9
| # this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__);
print"dirname(__FILE__) -> $rel_dirname\
";
# this gives the slightly wrong answer, but in the form I want
my $full_filepath = abs_path($0);
print"abs_path(\\$0) -> $full_filepath\
"; |
__FILE__的问题在于它将打印核心模块" .pm"路径,而不必打印正在运行的" .cgi"或" .pl"脚本路径。我想这取决于您的目标是什么。
在我看来,Cwd仅需要为mod_perl更新。这是我的建议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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有效的外部模块,即使使用'../'也可以很好地工作:
1 2 3 4 5
| my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\\/]*)$/; #keep the filename only
print"self=$self\
"; |
测试:
1 2 3 4 5 6 7 8
| $ /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__)的问题是它不遵循符号链接。我必须在脚本中使用它来跟随符号链接到达实际的文件位置。
1 2 3 4 5 6 7 8
| use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
$script_dir = dirname(readlink(__FILE__));
}
else {
$script_dir = dirname(__FILE__);
} |
实际上,所有无库解决方案都无法通过多种方式来编写路径(请考虑../或/bla/x/../bin/./x/../等。我的解决方案看起来像我有一个怪癖:我不知道为什么我必须两次运行替换项,否则我会得到一个虚假的" ./"或" ../"。对我来说似乎很健壮。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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"。以下功能提供了解决方案:
1 2 3 4 5 6 7 8 9 10
| 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));
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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 \
" ;
$ScriptAbsolutPath =~ m/^(.*)(\\\\|\\/)(.*)\\.([a-z]*)/;
$RunDir = $1 ;
#debug print"\\$1 is $1 \
" ;
#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 \
" ;
return $ProductBaseDir ;
} #eof sub |
$^X怎么了?
1 2 3
| #!/usr/bin/env perl
print"This is executed by $^X\
"; |
将为您提供正在使用的Perl二进制文件的完整路径。
翻转
在* nix上,您可能具有" whereis"命令,该命令在$ PATH中搜索具有给定名称的二进制文件。如果$ 0不包含完整路径名,则运行whereis $ scriptname并将结果保存到变量中将告诉您脚本的位置。