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

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

у кого-нибудь есть решение?

20 ответов


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

use Cwd 'abs_path';
print abs_path();

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

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


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

  • является текущим исполняемым скриптом, предоставленным POSIX, относительно текущего рабочего каталога, если скрипт находится на или ниже CWD
  • кроме того, cwd(), getcwd() и abs_path() предусмотрены Cwd модуль и сказать вам, где скрипт запускается из
  • модуль FindBin предоставляет $Bin & $RealBin переменные, которые обычно - путь к исполняемому скрипту; этот модуль также предоставляет $Script & $RealScript это название скрипта
  • __FILE__ - это фактический файл, с которым интерпретатор Perl имеет дело во время компиляции, включая его полный путь.

Я видел первые три ( на Cwd и FindBin модуль) сбой при mod_perl эффектно, производить бесполезный выход как '.' или пустая строка. В таких условиях, я использую __FILE__ и получить путь от использования File::Basename модуль:

use File::Basename;
my $dirname = dirname(__FILE__);

Use File::Spec;
File::Spec->rel2abs( __FILE__ );

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


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

#!/usr/bin/perl
use FindBin;

 = "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();
print "$path\n";

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

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

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


несколько коротких справочная информация:

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

но вам не нужен исполняемый файл Perl, который на самом деле работает, но скрипт, который он выполняет. И Perl должен знать, где сценарий, чтобы найти его. Он хранит это в __FILE__, а из API Unix. Это все еще может быть относительный путь, поэтому примите предложение Марка и канонизируйте его с 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() функция on . Эту функцию можно найти в File:: Spec.


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

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

#!/usr/bin/perl -w
use strict;


my $path = ;
$path =~ s/\.\///g;
if ($path =~ /\//){
  if ($path =~ /^\//){
    $path =~ /^((\/[^\/]+){1,}\/)[^\/]+$/;
    $path = ;
    }
  else {
    $path =~ /^(([^\/]+\/){1,})[^\/]+$/;
    my $path_b = ;
    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 =  if  =~
/\([^\]*)$|\/([^\/]*)$/;

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() ; 
        #debug print "$ScriptAbsolutPath is $ScriptAbsolutPath \n" ;
        $ScriptAbsolutPath =~ m/^(.*)(\|\/)(.*)\.([a-z]*)/; 
        $RunDir =  ; 
        #debug print "$1 is  \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__ это то, что он будет печатать основной модуль ".ПМ " путь не обязательно тот ".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 .='/'. if  =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";

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

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

все решения без библиотек фактически не работают более чем для нескольких способов написания пути (подумайте ../ или / bla/x/../закром.//икс././ п. Мое решение выглядит ниже. У меня есть одна странность: Я не имею ни малейшего представления, почему я должен запускать замену дважды. Если я этого не сделаю, то получу фальшивку "./" или." ./". Кроме того, он кажется мне вполне надежным.

  my $callpath = ;
  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/(.*)\/([^\/]+)//;

что с $^X ?

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

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

Эверт


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