У меня есть Perl script и вам нужно определить полный путь и имя файла script во время выполнения. Я обнаружил, что в зависимости от того, как вы называете script $0
, меняется и иногда содержит fullpath+filename
, а иногда просто filename
. Поскольку рабочий каталог также может меняться, я не могу представить способ надежного получения fullpath+filename
script.
Кто-нибудь получил решение?
$0 обычно является именем вашей программы, поэтому как насчет этого?
use Cwd 'abs_path';
print abs_path($0);
Мне кажется, что это должно работать, поскольку abs_path знает, используете ли вы относительный или абсолютный путь.
Обновить Для тех, кто читает эти годы спустя, вы должны прочитать ответ Дрю ниже. Это намного лучше, чем мое.
Существует несколько способов:
$0
- это выполняемый в настоящее время script, предоставляемый POSIX, относительно текущего рабочего каталога, если script находится в или ниже CWDcwd()
, getcwd()
и abs_path()
предоставляются модулем Cwd
и сообщают вам, где script выполняется изFindBin
содержит переменные $Bin
и $RealBin
, которые обычно являются путем выполнения script; этот модуль также предоставляет $Script
и $RealScript
, которые являются именами script__FILE__
- это фактический файл, с которым сталкивается интерпретатор Perl во время компиляции, включая полный путь.Я видел первые три ($0
, Cwd
и FindBin
) не удается выполнить mod_perl
эффектно, производя бесполезный вывод, например '.'
или пустую строку. В таких средах я использую __FILE__
и получаю путь от этого с помощью File::Basename
:
use File::Basename;
my $dirname = dirname(__FILE__);
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";
Вы можете использовать FindBin, Cwd, File:: Basename или их комбинация. Все они находятся в базовом дистрибутиве 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
для этого.
chdir()
во время компиляции?
Короткий фон:
К сожалению, Unix API не предоставляет запущенной программе полный путь к исполняемому файлу. Фактически, исполняемая вами программа может предоставить все, что захочет, в поле, которое обычно сообщает вашей программе, что это такое. Есть, как и все ответы, различные эвристики для поиска вероятных кандидатов. Но ничего, кроме поиска всей файловой системы, всегда будет работать, и даже это не удастся, если исполняемый файл будет перемещен или удален.
Но вы не хотите, чтобы исполняемый файл Perl выполнялся, но выполнялся script. И Perl должен знать, где найти script. Он хранит это в __FILE__
, а $0
- из Unix API. Это может быть относительный путь, поэтому возьмите предложение Mark и канонизируйте его с помощью File::Spec->rel2abs( __FILE__ );
__FILE__
прежнему дает мне относительный путь. то есть "."
Вы пробовали:
$ENV{'SCRIPT_NAME'}
или
use FindBin '$Bin';
print "The script is located in $Bin.\n";
Это действительно зависит от того, как он вызывается, и если он CGI или запускается из обычной оболочки и т.д.
Чтобы получить путь к каталогу, содержащему мой script, я использовал комбинацию уже полученных ответов.
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;
my $dir = dirname(File::Spec->rel2abs(__FILE__));
Нет необходимости использовать внешние модули, и только одна строка может содержать имя файла и относительный путь. Если вы используете модули и вам необходимо применить путь к каталогу script, то относительный путь будет достаточным.
$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
perlfaq8 отвечает на очень похожий вопрос с использованием функции rel2abs()
на $0
. Эта функция может быть найдена в файле:: Spec.
#!/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.
Все библиотечные решения на самом деле не работают более чем несколькими способами для написания пути (подумайте../или/bla/x/../bin/./x/../и т.д. My решение выглядит следующим образом: у меня есть одна причуда: у меня нет ни малейшего представления о том, почему я должен дважды запускать замены. Если я этого не сделаю, я получаю ложные "./" или "../". это кажется мне довольно надежным.
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/;
Проблема с использованием dirname(__FILE__)
заключается в том, что она не соответствует символическим ссылкам. Мне пришлось использовать это для моего script, чтобы следовать символической ссылке в фактическое местоположение файла.
use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
$script_dir = dirname(readlink(__FILE__));
}
else {
$script_dir = dirname(__FILE__);
}
Без каких-либо внешних модулей, действительных для оболочки, хорошо работает даже с '../':
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
Проблема с __FILE__
заключается в том, что он будет печатать основной модуль ".pm" не обязательно в запущенном пути ".cgi" или ".pl" script. Я думаю, это зависит от вашей цели.
Мне кажется, что 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;
Пожалуйста, добавьте любые предложения.
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 \ to / 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
Что случилось с $^X
?
#!/usr/bin/env perl<br>
print "This is executed by $^X\n";
Дает вам полный путь к используемому двоичному файлу Perl.
Эверт
В * nix у вас, вероятно, есть команда "whereis", которая ищет ваш PATH, ищущий двоичный файл с заданным именем. Если $0 не содержит полного имени пути, запуск whereis $scriptname и сохранение результата в переменной должен указать вам, где находится script.
realpath
, который является синонимомabs_path
, если вы предпочитаете имя без подчеркивания