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

186

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

有人有解决方案吗?


我知道这是很久以前的事情了,但我只是在寻找一种在Windows上使用Perl完成此操作的方法,并且对我的解决方案感到非常满意。#!/usr/bin/perl -w my @catalog=dir; $myHome = substr($catalog[3],14); $myHome = &rtrim($myHome); print qq(<$myHome>\n);

右侧修剪函数以删除尾随空格

sub rtrim { my $string = shift; $string =~ s/\s+$//; return $string; } 只是想分享一下。
- user1210923
23个回答

283

有几种方法:

  • $0 是 POSIX 提供的当前执行脚本,如果脚本在当前工作目录下或以下,则相对于当前工作目录
  • 此外,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__);

2
这真的是最好的解决方案,特别是如果您已经修改了$0。 - Caterham
10
看起来需要使用 abs_path 和 FILE 一起,因为它可能仅包含路径名。 - Aftershock
6
可能是因为这个答案中最重要的建议(使用__FILE__dirname)并没有按预期运行?我最终获得了相对于脚本执行位置的路径,而被接受的答案给出了完整的绝对路径。 - Izkata
13
dirname(__FILE__) 不会跟随符号链接,所以如果你链接了可执行文件,并希望找到安装位置中的其他文件的位置,你需要检查 if( -l __FILE__),然后使用 dirname(readlink(__FILE__)) - DavidGamba
4
使用此命令可以找到模块首次包含在标准模块中的时间:perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo。 对于 File::Basename 模块,它是在 Perl 5.0.0 中发布的,该版本发布于90年代末 - 我认为现在使用它是安全的。 - Drew Stephens
显示剩余11条评论

150

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

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

我觉得这应该可行,因为abs_path知道你是使用相对路径还是绝对路径。

更新对于那些几年后读到这篇文章的人,你应该阅读Drew的答案,他的答案比我的好多了。


12
在Windows上使用ActiveState Perl时,$0通常包含反斜杠,而abs_path返回正斜杠,因此需要快速执行“tr///\/;”来修复它。 - Chris Madden
3
补充一下,realpathabs_path 的同义词,如果你更喜欢没有下划线的名称,可以选择使用它。 - vol7ron
@Chris,你向Cwd模块的维护者报告了这个问题吗?这似乎是Windows适配性的问题。 - Znik
1
我还有另一个问题:perl -e 'use Cwd "abs_path";print abs_path($0);' 打印出 /tmp/-e - leonbloy
2
当您使用 -e 参数在命令行中执行 Perl 脚本时,Perl 会创建一个临时文件以存储您的内联脚本。在您的情况下,它看起来位于 /tmp 目录。您对结果有何期望? - GreenGiant
显示剩余3条评论

39

然后使用dirname()将其包装起来以获取绝对路径 - 这正是我所需要的! - David H.

16

我认为你要找的模块是FindBin:

#!/usr/bin/perl
use FindBin;

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

11
你可以使用FindBin, Cwd, File::Basename或它们的组合。据我所知,它们都在Perl的基本分发中。
我过去使用过Cwd:
Cwd:
use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";

1
@bmdhacks,你说得对。假设你没有改变0美元。例如,当脚本启动时(在初始化块中)或者在其他地方,只要你不改变$0,就可以执行上述工作。但是$0是改变进程描述在“ps”Unix工具下可见的绝佳方式 :) 这可以显示当前进程状态等。这取决于程序员的目的 :) - Znik

10
获取$0__FILE__的绝对路径是您想要的。唯一的麻烦是,如果有人做了一个chdir(),并且$0是相对的-那么你需要在BEGIN{}中获取绝对路径以避免任何意外情况。 FindBin试图更进一步,在$PATH中寻找与basename($0)匹配的东西,但有时会做出太出乎意料的事情(特别是:当文件在当前工作目录中时)。 File::FuFile::Fu->program_nameFile::Fu->program_dir来实现此功能。

有人会在编译时(永久性地)使用 chdir() 吗? - SamB
在脚本开始时,只需根据当前目录和$0执行所有工作。 - Znik

8

一些简短的背景:

不幸的是,Unix API没有提供运行程序的完整路径。实际上,执行您的程序的程序可以在通常告诉您的程序它是什么的字段中提供任何内容。正如所有答案所指出的那样,有各种启发式方法来查找可能的候选项。但除了搜索整个文件系统外,没有什么方法总是有效的,即使这样做也会失败,如果可执行文件被移动或删除。

但是,您不想要运行的Perl可执行文件,而是要执行的脚本。Perl需要知道脚本的位置才能找到它。它将其存储在__FILE__中,而$0来自Unix API。这仍然可能是相对路径,因此采用Mark的建议使用File::Spec->rel2abs(__FILE__);来规范化它。


__FILE__ 仍然给我相对路径,即'.'。 - felwithe

6

您尝试过:

$ENV{'SCRIPT_NAME'}

或者

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

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


当脚本在控制台运行时,$ENV{'SCRIPT_NAME'}为空。 - Putnik
1
这是个坏主意,因为SCRIPT_NAME环境变量取决于你使用的shell。这与Windows cmd.exe完全不兼容,并且在直接从其他二进制文件调用脚本时也不兼容。不能保证该变量已设置。上述方法更加可用。 - Znik

6
为了获取包含我的脚本的目录路径,我使用了已经给出的答案的组合。
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;

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

2

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

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

如果您像“./myscript.pl”一样运行它,它不会提供脚本的正确完整路径,因为它只会显示“。”。但我仍然喜欢这个解决方案。 - Keve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接