Как мне получить полный путь к исполняемому Perl-скрипту?
Вопрос
У меня есть 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";
Вы могли бы использовать Найди Бин, Cwd, Файл::Базовое имя, или их комбинация.Все они находятся в базовом дистрибутиве Perl IIRC.
В прошлом я использовал Cwd:
Cwd:
use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\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 или запускается из обычной оболочки и т.д.
Чтобы получить путь к каталогу, содержащему мой скрипт, я использовал комбинацию уже приведенных ответов.
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;
my $dir = dirname(File::Spec->rel2abs(__FILE__));
perlfaq8 отвечает на очень похожий вопрос с использованием rel2abs()
функция включена $0
.Эту функцию можно найти в файле::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;
Пожалуйста, добавляйте любые предложения.
Без каких-либо внешних модулей, допустимый для оболочки, хорошо работает даже с '../':
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 / ../ etc.Мое решение выглядит следующим образом.У меня есть одна причуда:Я не имею ни малейшего представления, почему я должен выполнять замену дважды.Если я этого не сделаю, я получу ложное "./" или "../".Кроме того, мне он кажется довольно надежным.
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 не содержит полного имени пути, запуск где $scriptname и сохранение результата в переменную должны сообщить вам, где находится скрипт.