it-swarm-pt.tech

Como obtenho o caminho completo para um script Perl que está sendo executado?

Eu tenho o script Perl e preciso determinar o caminho completo e o nome do arquivo do script durante a execução. Descobri que, dependendo de como você chama o script, $0 varia e, às vezes, contém fullpath+filename e, às vezes, apenas filename. Como o diretório de trabalho também pode variar, não consigo pensar em uma maneira de obter com segurança o fullpath+filename do script.

Alguém tem uma solução?

156
Chris Madden

Existem algumas maneiras:

  • $0 é o script atualmente em execução conforme fornecido pelo POSIX, relativo ao diretório de trabalho atual, se o script estiver no CWD ou abaixo dele
  • Além disso, cwd(), getcwd() e abs_path() são fornecidos pelo módulo Cwd e informam onde o script está sendo executado.
  • O módulo FindBin fornece as variáveis ​​$Bin & $RealBin que geralmente são o caminho para o script em execução; este módulo também fornece $Script & $RealScript que são o nome do script
  • __FILE__ é o arquivo real com o qual o interpretador Perl lida durante a compilação, incluindo seu caminho completo.

Eu vi os primeiros três ( $0 , o Cwd module e FindBin module) falharem sob mod_Perl espetacularmente, produzindo saída sem valor, como '.' ou uma string vazia. Em tais ambientes, eu uso __FILE__ e obtenho o caminho a partir disso usando o File::Basename module:

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

$ 0 é tipicamente o nome do seu programa, então que tal?

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

Parece-me que isso deve funcionar como abs_path sabe se você está usando um caminho relativo ou absoluto.

Atualização Para quem lê este ano mais tarde, você deve ler a resposta de Drew abaixo. É muito melhor que o meu.

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

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

34
Mark

Eu acho que o módulo que você está procurando é o FindBin:

#!/usr/bin/Perl
use FindBin;

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

Você poderia usar FindBin , Cwd , File :: Basename , ou uma combinação deles. Eles estão todos na distribuição base do Perl IIRC.

Eu usei o Cwd no passado:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
11
Benjamin W. Smith

Obtendo o caminho absoluto para $0 ou __FILE__ é o que você deseja. O único problema é que se alguém fez uma chdir() e o $0 foi relativo - então você precisa obter o caminho absoluto em BEGIN{} para evitar surpresas.

FindBin tenta ir um pouco melhor e rastejar ao redor do $PATH para algo que combine com basename($0), mas há momentos em que isso faz coisas muito surpreendentes (especificamente: quando o arquivo está "bem na sua frente" no cwd).

File::Fu tem File::Fu->program_name e File::Fu->program_dir para isso.

9
Eric Wilhelm

Algum fundo curto:

Infelizmente, a API do Unix não fornece um programa em execução com o caminho completo para o executável. Na verdade, o programa que executa o seu pode fornecer o que quiser no campo que normalmente diz ao seu programa o que ele é. Há, como todas as respostas apontam, várias heurísticas para encontrar possíveis candidatos. Mas nada menos do que pesquisar todo o sistema de arquivos sempre funcionará, e mesmo isso falhará se o executável for movido ou removido.

Mas você não quer o executável Perl, que é o que está sendo executado, mas o script está sendo executado. E Perl precisa saber onde o script é para encontrá-lo. Ele armazena isso em __FILE__, enquanto $0 é da API do Unix. Isso ainda pode ser um caminho relativo, então pegue a sugestão de Mark e canonize-a com File::Spec->rel2abs( __FILE__ );

7
wnoise

Para obter o caminho para o diretório que contém o meu script, usei uma combinação de respostas já fornecidas.

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

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

Você tentou:

$ENV{'SCRIPT_NAME'}

ou

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

Isso realmente depende de como ele está sendo chamado e se é CGI ou está sendo executado a partir de um Shell normal, etc.

6
Sean

perlfaq8 responde uma pergunta muito semelhante com o uso da função rel2abs() em $0. Essa função pode ser encontrada em File :: Spec.

2
moritz

Não há necessidade de usar módulos externos, com apenas uma linha você pode ter o nome do arquivo e o caminho relativo. Se você estiver usando módulos e precisar aplicar um caminho relativo ao diretório de script, o caminho relativo será suficiente.

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

Você está procurando por isso ?:

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

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

A saída ficará assim:

You are running MyFileName.pl now.

Funciona tanto no Windows quanto no Unix.

1
Yong Li
#!/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

1
mkc
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 
0
Yordan Georgiev

Nenhuma das respostas "top" foi certa para mim. O problema com o uso de FindBin '$ Bin' ou Cwd é que eles retornam caminho absoluto com todos os links simbólicos resolvidos. No meu caso, eu precisava do caminho exato com links simbólicos presentes - o mesmo que retorna o comando Unix "pwd" e não "pwd -P". A função a seguir fornece a solução:

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));
}
0
drjumper

O problema de usar apenas dirname(__FILE__) é que ele não segue links simbólicos. Eu tive que usar isso para o meu script para seguir o link simbólico para o local real do arquivo.

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

O problema com __FILE__ é que ele irá imprimir o caminho do módulo principal ".pm" não necessariamente o caminho de script ".cgi" ou ".pl" que está sendo executado. Eu acho que depende do seu objetivo.

Parece-me que Cwd só precisa ser atualizado para mod_Perl. Aqui está a minha sugestão:

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;

Por favor, adicione alguma sugestão.

0
Jonathan

Sem módulos externos, válidos para o Shell, funciona bem mesmo com '../':

my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";

teste:

$ /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
Putnik

Todas as soluções livres de bibliotecas na verdade não funcionam mais do que algumas maneiras de escrever um caminho (pense em .../ou /bla/x/../bin/./x/../ etc. Minha solução parece Eu tenho uma peculiaridade: Eu não tenho a menor idéia do porquê de ter que executar as substituições duas vezes, se não, eu recebo um falso "./" ou "../". parece bastante robusto para mim.

  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
Elmar