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


Answers

Есть несколько способов:

  • $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__);
Question

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

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




#!/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




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




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

#!/usr/bin/perl
use FindBin;

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



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 



Некоторый короткий фон:

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

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




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




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



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

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

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




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

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