Como obtenho o caminho completo para um script Perl em execução?


168

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, $0varia e às vezes contém fullpath+filenamee, às vezes, apenas filename. Como o diretório de trabalho também pode variar, não consigo pensar em uma maneira de obter fullpath+filenameo script de maneira confiável .

Alguém tem uma solução?

Respostas:


251

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 Cwdmódulo e dizer-lhe onde o script está sendo executado a partir de
  • O módulo FindBinfornece as $Bin& $RealBinvariáveis ​​que geralmente são o caminho para o script em execução; este módulo também fornece $Script& $RealScriptesse é 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 três primeiros ( $0, o Cwdmódulo e o FindBinmódulo) falharem mod_perlespetacularmente, produzindo uma saída sem valor, como '.'uma string vazia. Nesses ambientes, eu uso __FILE__e obtenho o caminho usando o File::Basenamemódulo:

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

2
Esta é realmente a melhor solução, especialmente se você já tem um modificado $ 0
Caterham

8
Parece que abs_path precisa ser usado com _____FILE_____, pois pode conter o nome apenas com o caminho.
Aftershock 23/03

6
@vicTROLLA Provavelmente porque a maior recomendação desta resposta (usar dirname com __FILE__) não funciona como o esperado? Termino com o caminho relativo de onde o script foi executado, enquanto a resposta aceita me fornece o caminho absoluto completo.
Izkata

10
dirname(__FILE__)não segue links simbólicos; portanto, se você vinculou o arquivo executável e onde espera encontrar o local de outro arquivo no local de instalação, é necessário verificar if( -l __FILE__)e depois dirname(readlink(__FILE__)).
DavidG

3
@IliaRostovtsev Você pode encontrar quando um módulo foi incluído pela primeira vez nos módulos padrão com este encantamento: perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo. Pois File::Basenameesse foi o Perl 5.0.0, que foi lançado no final dos anos 90 - acho que é preciso usar agora.
de Drew Stephens

145

Normalmente, $ 0 é o nome do seu programa, e quanto a isso?

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 ler este ano depois, leia a resposta de Drew . É muito melhor que o meu.


11
Pequeno comentário, sobre o activestate perl no windows $ 0 normalmente contém barras invertidas e abs_path retornam barras, então um rápido "tr / \ // \\ /;" foi necessário para consertá-lo.
Chris Madden

3
queria acrescentar que há um realpath, que é um sinônimo para abs_path, no caso de você preferir o nome não-sublinhado
vol7ron

@ Chris, você relatou um bug ao mantenedor do módulo Cwd? Parece bug de adoção do Windows.
Znik

1
Outro problema que tenho: perl -e 'use Cwd "abs_path";print abs_path($0);' prints #/tmp/-e
leonbloy 15/05

2
@leonbloy Quando você executa um script embutido (com -e), acredito que o perl cria um arquivo temporário para armazenar seu script embutido. Parece que a localização, no seu caso, é /tmp. O que você esperava que fosse o resultado?
GreenGiant


16

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";

11

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

Eu usei Cwd no passado:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";

@bmdhacks, você está certo. Presunção é que você não mudou 0 $. Por exemplo, você trabalha acima assim que o script é iniciado (no bloco de inicialização) ou em outro lugar quando você não altera $ 0. Mas $ 0 é excelente maneira de descrição processo de mudança sob visível 'ps' ferramenta unix :) Isso pode mostrar o status do processo Curren, etc. Este é dependia propósito programador :)
Znik

9

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

FindBintenta melhorar e procurar $PATHalgo parecido com o basename($0), mas há momentos em que isso faz coisas muito surpreendentes (especificamente: quando o arquivo está "bem na sua frente" no cwd).

File::Futem File::Fu->program_namee File::Fu->program_dirpara isso.


É realmente provável que alguém seja tão tolo quanto (permanentemente) chdir()em tempo de compilação?
SamB

Simplesmente faça todos os trabalhos baseados no diretório atual e US $ 0 no início do script.
Znik 03/03

7

Alguns antecedentes curtos:

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

Mas você não deseja o executável Perl, que é o que está realmente sendo executado, mas o script que está sendo executado. E o Perl precisa saber onde o script está. Ele armazena isso __FILE__enquanto $0é da API Unix. Ainda pode ser um caminho relativo, então pegue a sugestão de Mark e canonize-a comFile::Spec->rel2abs( __FILE__ );


__FILE__ainda me dá um caminho relativo. ie '.'.
Feltro #

6

Você tentou:

$ENV{'SCRIPT_NAME'}

ou

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

Realmente depende de como está sendo chamado e se é CGI ou está sendo executado a partir de um shell normal, etc.


$ ENV {'SCRIPT_NAME'} está vazio quando o script está sendo executado no console
Putnik

Má ideia porque o ambiente de SCRIPT_NAME depende do shell que você está usando. Isso é totalmente incompatível com o Windows cmd.exe e incompatível quando você chama o script diretamente de outros binários. Não há garantia de que este wariável esteja definido. As formas acima são muito mais utilizáveis.
Znik

6

Para obter o caminho para o diretório que contém 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__));

2

O perlfaq8 responde a uma pergunta muito semelhante ao usar a rel2abs()função on $0. Essa função pode ser encontrada em File :: Spec.


2

Não é necessário 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 scripts, o caminho relativo será suficiente.

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

Ele não fornece o caminho completo adequado do script se você o executar como "./myscript.pl", pois mostraria apenas "." em vez de. Mas ainda gosto desta solução.
Keve

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
Por favor, não responda apenas com o código. Por favor, explique por que essa é a resposta correta.
Lee Taylor

1

Você está procurando isso ?:

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

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

A saída terá a seguinte aparência:

You are running MyFileName.pl now.

Funciona no Windows e no Unix.


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 \'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 

Embora uma resposta somente de origem possa resolver a pergunta do usuário, ela não os ajuda a entender por que ela funciona. Você deu ao usuário um peixe, mas, em vez disso, você deve ensiná-lo a pescar.
the Tin Man

0

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

Parece-me que Cwdsó 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 sugestões.


0

Sem nenhum módulo externo, válido para 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

O que quando você chama o link simbólico? Cwd funciona excelente com este caso.
Znik

0

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

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

0

Na verdade, todas as soluções sem biblioteca não funcionam por mais de algumas maneiras de escrever um caminho (pense ../ ou /bla/x/../bin/./x/../ etc. Minha solução parece Tenho uma peculiaridade: não tenho a menor idéia do porquê de executar as substituições duas vezes. Se não tiver, recebo um "./" ou "../" falso. Além disso, ele 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

Nenhuma das respostas "principais" foi adequada 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 seguinte função 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

No Windows, usar dirnamee abs_pathjuntos funcionou melhor para mim.

use File::Basename;
use Cwd qw(abs_path);

# absolute path of the directory containing the executing script
my $abs_dirname = dirname(abs_path($0));
print "\ndirname(abs_path(\$0)) -> $abs_dirname\n";

aqui está o porquê:

# this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__); 
print "dirname(__FILE__) -> $rel_dirname\n"; 

# this gives the slightly wrong answer, but in the form I want 
my $full_filepath = abs_path($0);
print "abs_path(\$0) -> $full_filepath\n";

-2

O que há de errado $^X?

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

Daria a você o caminho completo para o binário Perl sendo usado.

Evert


1
Ele dá caminho para o binário Perl, enquanto caminho para um script necessário
Putnik

-5

No * nix, você provavelmente possui o comando "whereis", que pesquisa seu $ PATH procurando um binário com um nome específico. Se $ 0 não contiver o nome completo do caminho, executar whereis $ scriptname e salvar o resultado em uma variável deve informar onde o script está localizado.


Isso não funcionará, pois $ 0 também pode retornar um caminho relativo para o arquivo: ../perl/test.pl
Lathan

o que acontecerá se o script executável estiver fora do PATH?
Znik 17/10
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.