题
我有 Perl 脚本,需要在执行期间确定脚本的完整路径和文件名。我发现这取决于你如何调用脚本 $0
各不相同,有时包含 fullpath+filename
有时只是 filename
. 。因为工作目录也可能有所不同,所以我想不出一种可靠的方法来获取 fullpath+filename
脚本的。
有人有解决办法吗?
解决方案
有以下几种方法:
$0
是 POSIX 提供的当前正在执行的脚本,如果该脚本位于或低于 CWD,则相对于当前工作目录- 此外,
cwd()
,getcwd()
和abs_path()
由提供Cwd
模块并告诉您脚本从哪里运行 - 该模块
FindBin
提供了$Bin
&$RealBin
变量 通常 是执行脚本的路径;该模块还提供$Script
&$RealScript
这是脚本的名称 __FILE__
是 Perl 解释器在编译期间处理的实际文件,包括其完整路径。
我看过前三部($0
, , 这 Cwd
模块和 FindBin
模块)失败 mod_perl
惊人地,产生毫无价值的产出,例如 '.'
或空字符串。在这样的环境下,我使用 __FILE__
并使用从中获取路径 File::Basename
模块:
use File::Basename;
my $dirname = dirname(__FILE__);
其他提示
$0 通常是您的程序的名称,那么这个怎么样?
use Cwd 'abs_path';
print abs_path($0);
在我看来,这应该可以工作,因为 abs_path 知道您使用的是相对路径还是绝对路径。
更新 对于多年后阅读此书的人,您应该阅读下面德鲁的回答。比我的好多了。
Use File::Spec;
File::Spec->rel2abs( __FILE__ );
我认为您正在寻找的模块是 FindBin:
#!/usr/bin/perl
use FindBin;
$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";
获取绝对路径 $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。这仍然可以是相对路径,因此请采纳马克的建议并将其规范化 File::Spec->rel2abs( __FILE__ );
你有没有尝试过:
$ENV{'SCRIPT_NAME'}
或者
use FindBin '$Bin';
print "The script is located in $Bin.\n";
这实际上取决于它的调用方式以及它是 CGI 还是从普通 shell 运行等。
为了获取包含我的脚本的目录的路径,我使用了已经给出的答案的组合。
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;
my $dir = dirname(File::Spec->rel2abs(__FILE__));
perlfaq8 使用以下命令回答了一个非常相似的问题 rel2abs()
功能于 $0
. 。该函数可以在 File::Spec 中找到。
无需使用外部模块,只需一行即可获得文件名和相对路径。如果您正在使用模块并且需要应用相对于脚本目录的路径,则相对路径就足够了。
$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/^(.*)(\\|\/)(.*)\.([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
问题在于 __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;
请添加任何建议。
没有任何外部模块,对 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__);
}
所有无库解决方案实际上都不适用于多种编写路径的方法(例如 ../ 或 /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/;
“最重要”的答案都不适合我。使用 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));
}
出了什么问题 $^X
?
#!/usr/bin/env perl<br>
print "This is executed by $^X\n";
将为您提供正在使用的 Perl 二进制文件的完整路径。
翻转
在 *nix 上,您可能有“whereis”命令,它会搜索您的 $PATH 查找具有给定名称的二进制文件。如果 $0 不包含完整路径名,则运行 whereis $scriptname 并将结果保存到变量中应该会告诉您脚本所在的位置。