175

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

有人有解决方案吗?

4

23 回答 23

265

有几种方法:

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

我已经看到前三个($0Cwd模块和FindBin模块)在壮观的情况下失败mod_perl,产生无价值的输出,例如'.'或空字符串。在这样的环境中,我使用模块__FILE__并从中获取路径File::Basename

use File::Basename;
my $dirname = dirname(__FILE__);
于 2008-09-18T07:30:54.413 回答
148

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

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

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

更新对于今年以后阅读的任何人,您都应该阅读Drew 的回答。这比我的好多了。

于 2008-09-17T16:19:48.940 回答
38
use File::Spec;
File::Spec->rel2abs( __FILE__ );

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

于 2008-09-17T16:31:53.327 回答
16

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

#!/usr/bin/perl
use FindBin;

$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";
于 2008-09-17T16:22:39.853 回答
11

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

我过去使用 Cwd:

密码:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
于 2008-09-17T16:29:15.110 回答
9

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

FindBin试图做得更好,并在$PATHbasename($0).

File::FuFile::Fu->program_name并且File::Fu->program_dir为此。

于 2008-09-18T07:45:24.393 回答
7

一些简短的背景:

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

但是你不想要 Perl 可执行文件,它是实际运行的,而是它正在执行的脚本。Perl 需要知道脚本在哪里可以找到它。它存储在 中__FILE__,而$0来自 Unix API。这仍然可以是相对路径,因此请采纳 Mark 的建议并将其规范化为File::Spec->rel2abs( __FILE__ );

于 2008-09-17T16:52:24.057 回答
6

你有没有尝试过:

$ENV{'SCRIPT_NAME'}

或者

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

这实际上取决于它的调用方式以及它是 CGI 还是从普通 shell 运行等。

于 2008-09-17T16:21:33.617 回答
6

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

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

my $dir = dirname(File::Spec->rel2abs(__FILE__));
于 2013-07-18T23:12:45.777 回答
2

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

于 2008-09-17T16:34:54.600 回答
2

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

$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
于 2014-07-01T16:19:13.760 回答
1

你在找这个吗?:

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

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

输出将如下所示:

You are running MyFileName.pl now.

它适用于 Windows 和 Unix。

于 2011-04-01T16:27:08.150 回答
1
#!/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

于 2012-11-26T14:01:34.817 回答
1

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

use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
  $script_dir = dirname(readlink(__FILE__));
}
else {
  $script_dir = dirname(__FILE__);
}
于 2014-04-18T07:56:16.510 回答
0
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 
于 2011-08-09T13:36:04.970 回答
0

问题__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;

请添加任何建议。

于 2013-12-13T11:57:26.893 回答
0

没有任何外部模块,对 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
于 2014-02-06T14:39:11.593 回答
0

所有无库的解决方案实际上并不能用于编写路径的几种方法(想想 ../ 或 /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/;
于 2014-09-04T10:32:56.000 回答
0

没有一个“最佳”答案适合我。使用 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));
}
于 2018-10-23T08:51:38.367 回答
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";
于 2019-10-16T15:39:42.603 回答
0
use File::Basename;
use Cwd 'abs_path';
print dirname(abs_path(__FILE__)) ;

德鲁的回答给了我:

'。'

$ cat >testdirname
use File::Basename;
print dirname(__FILE__);
$ perl testdirname
.$ perl -v

This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-gnu-thread-multi][1]
于 2020-08-23T17:39:37.503 回答
-2

有什么问题$^X

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

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

翻转

于 2013-12-03T10:48:48.963 回答
-5

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

于 2008-09-17T16:18:38.943 回答