Как получить полный путь к исполняемому Perl-скрипту?

140

У меня есть Perl script и вам нужно определить полный путь и имя файла script во время выполнения. Я обнаружил, что в зависимости от того, как вы называете script $0, меняется и иногда содержит fullpath+filename, а иногда просто filename. Поскольку рабочий каталог также может меняться, я не могу представить способ надежного получения fullpath+filename script.

Кто-нибудь получил решение?

Теги:
location
path

20 ответов

136
Лучший ответ

$0 обычно является именем вашей программы, поэтому как насчет этого?

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

Мне кажется, что это должно работать, поскольку abs_path знает, используете ли вы относительный или абсолютный путь.

Обновить Для тех, кто читает эти годы спустя, вы должны прочитать ответ Дрю ниже. Это намного лучше, чем мое.

  • 11
    Небольшой комментарий о perl activestate в windows $ 0 обычно содержит обратную косую черту, а abs_path возвращает прямую косую черту, поэтому быстрый "tr / \ // \\ /;" было необходимо исправить это.
  • 3
    хотел бы добавить, что есть realpath , который является синонимом abs_path , если вы предпочитаете имя без подчеркивания
Показать ещё 6 комментариев
190

Существует несколько способов:

  • $0 - это выполняемый в настоящее время script, предоставляемый POSIX, относительно текущего рабочего каталога, если script находится в или ниже CWD
  • Кроме того, cwd(), 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__);
  • 2
    Это действительно лучшее решение, особенно если у вас уже есть модифицированный $ 0
  • 1
    Upvote. Не совсем уверен, почему этот ответ не был принят, так как принятый выше ответ не работает, учитывая, что он включает удаление имени сценария с конца пути.
Показать ещё 10 комментариев
35
Use File::Spec;
File::Spec->rel2abs( __FILE__ );

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

16

Я думаю, что модуль, который вы ищете, - FindBin:

#!/usr/bin/perl
use FindBin;

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

Вы можете использовать FindBin, Cwd, File:: Basename или их комбинация. Все они находятся в базовом дистрибутиве Perl IIRC.

Я использовал Cwd в прошлом:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
  • 3
    Это неверно, так как $ 0 можно изменить при запуске скрипта.
  • 1
    это здорово, у меня работает Cwd
Показать ещё 1 комментарий
8

Получение абсолютного пути к $0 или __FILE__ - это то, что вы хотите. Единственная проблема заключается в том, что кто-то сделал chdir() и $0 относительный - тогда вам нужно получить абсолютный путь в BEGIN{}, чтобы предотвратить любые неожиданности.

FindBin пытается лучше разобраться в $PATH для того, что соответствует basename($0), но бывают случаи, когда это делает слишком удивительные вещи (в частности: когда файл "прямо в перед вами" в cwd.)

File::Fu имеет File::Fu->program_name и File::Fu->program_dir для этого.

  • 0
    Действительно ли вероятно, что кто-то будет настолько глуп, чтобы (постоянно) использовать chdir() во время компиляции?
  • 0
    Просто сделайте все работы, основываясь на текущем каталоге и $ 0 в начале скрипта.
6

Короткий фон:

К сожалению, Unix API не предоставляет запущенной программе полный путь к исполняемому файлу. Фактически, исполняемая вами программа может предоставить все, что захочет, в поле, которое обычно сообщает вашей программе, что это такое. Есть, как и все ответы, различные эвристики для поиска вероятных кандидатов. Но ничего, кроме поиска всей файловой системы, всегда будет работать, и даже это не удастся, если исполняемый файл будет перемещен или удален.

Но вы не хотите, чтобы исполняемый файл Perl выполнялся, но выполнялся script. И Perl должен знать, где найти script. Он хранит это в __FILE__, а $0 - из Unix API. Это может быть относительный путь, поэтому возьмите предложение Mark и канонизируйте его с помощью File::Spec->rel2abs( __FILE__ );

  • 0
    __FILE__ прежнему дает мне относительный путь. то есть "."
6

Вы пробовали:

$ENV{'SCRIPT_NAME'}

или

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

Это действительно зависит от того, как он вызывается, и если он CGI или запускается из обычной оболочки и т.д.

  • 0
    $ ENV {'SCRIPT_NAME'} пусто, когда скрипт запущен на консоли
  • 0
    Плохая идея, потому что среда SCRIPT_NAME зависит от используемой вами оболочки. Это полностью несовместимо с windows cmd.exe и несовместимо при вызове сценария непосредственно из других двоичных файлов. Там нет никакой гарантии, эта гарантия установлена. Вышеуказанные способы гораздо более удобны в использовании.
5

Чтобы получить путь к каталогу, содержащему мой script, я использовал комбинацию уже полученных ответов.

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

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

Нет необходимости использовать внешние модули, и только одна строка может содержать имя файла и относительный путь. Если вы используете модули и вам необходимо применить путь к каталогу script, то относительный путь будет достаточным.

$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
  • 0
    Он не предоставляет правильный полный путь к скрипту, если вы запустите его как «./myscript.pl», так как он будет показывать только «.» вместо. Но мне все еще нравится это решение.
2

perlfaq8 отвечает на очень похожий вопрос с использованием функции rel2abs() на $0. Эта функция может быть найдена в файле:: Spec.

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

  • 4
    Пожалуйста, не просто отвечайте с кодом. Пожалуйста, объясните, почему это правильный ответ.
  • 0
    Хех .... ты разработал колесо! используйте модуль Cwd, ваш код будет проще.
1

Вы ищете это?:

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

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

Результат будет выглядеть так:

You are running MyFileName.pl now.

Он работает как с Windows, так и с Unix.

0

Все библиотечные решения на самом деле не работают более чем несколькими способами для написания пути (подумайте../или/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/;
0

Проблема с использованием dirname(__FILE__) заключается в том, что она не соответствует символическим ссылкам. Мне пришлось использовать это для моего script, чтобы следовать символической ссылке в фактическое местоположение файла.

use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
  $script_dir = dirname(readlink(__FILE__));
}
else {
  $script_dir = dirname(__FILE__);
}
0

Без каких-либо внешних модулей, действительных для оболочки, хорошо работает даже с '../':

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
  • 0
    Что когда вы вызываете символическую ссылку? Cwd отлично работает с этим делом.
0

Проблема с __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;

Пожалуйста, добавьте любые предложения.

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 \ 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 
  • 0
    Хотя ответ только на источник может решить вопрос пользователя, он не помогает им понять, почему он работает. Вы дали пользователю рыбу, но вместо этого вы должны научить его КАК ловить рыбу.
-1

Что случилось с $^X?

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

Дает вам полный путь к используемому двоичному файлу Perl.

Эверт

  • 1
    Он дает путь к бинарному Perl, а путь к скрипту требуется
-4

В * nix у вас, вероятно, есть команда "whereis", которая ищет ваш PATH, ищущий двоичный файл с заданным именем. Если $0 не содержит полного имени пути, запуск whereis $scriptname и сохранение результата в переменной должен указать вам, где находится script.

  • 0
    Это не будет работать, так как $ 0 может также вернуть относительный путь к файлу: ../perl/test.pl
  • 0
    что произойдет, если исполняемый скрипт находится вне PATH?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню