Come posso ottenere il percorso completo di uno script Perl in esecuzione?


168

Ho lo script Perl e ho bisogno di determinare il percorso completo e il nome file dello script durante l'esecuzione. Ho scoperto che a seconda di come si chiama lo script $0varia e talvolta contiene il fullpath+filenamee talvolta giusto filename. Poiché anche la directory di lavoro può variare, non riesco a pensare a un modo per ottenere in modo affidabile fullpath+filenamelo script.

Qualcuno ha una soluzione?

Risposte:


251

Ci sono alcuni modi:

  • $0 è lo script attualmente in esecuzione, come fornito da POSIX, relativo alla directory di lavoro corrente se lo script si trova nella CWD o al di sotto di essa
  • Inoltre, cwd(), getcwd()e abs_path()sono forniti dal Cwdmodulo e ti dirà dove lo script viene eseguito da
  • Il modulo FindBinfornisce le variabili $Bin& $RealBinche di solito sono il percorso dello script in esecuzione; questo modulo fornisce anche $Scripte $RealScriptche sono il nome dello script
  • __FILE__ è il file effettivo che l'interprete Perl gestisce durante la compilazione, incluso il suo percorso completo.

Ho visto i primi tre ( $0, il Cwdmodulo e il FindBinmodulo) fallire in modo mod_perlspettacolare, producendo un output senza valore come '.'o una stringa vuota. In tali ambienti, utilizzo __FILE__e ottengo il percorso da quello utilizzando il File::Basenamemodulo:

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

2
Questa è davvero la soluzione migliore, soprattutto se hai già modificato $ 0
Caterham

8
Sembra che abs_path debba essere usato con _____FILE_____ poiché può contenere il nome solo con il percorso.
Scossa di

6
@vicTROLLA Probabilmente perché la più grande raccomandazione da questa risposta (usando dirname con __FILE__) non funziona esattamente come previsto? Finisco con il relativo percorso da cui è stata eseguita la sceneggiatura, mentre la risposta accettata mi dà il percorso assoluto completo.
Izkata,

10
dirname(__FILE__)non segue i symlink, quindi se hai collegato il file eseguibile e dove speri di trovare la posizione di qualche altro file nella posizione di installazione devi controllare if( -l __FILE__)e quindi dirname(readlink(__FILE__)).
DavidG

3
@IliaRostovtsev Si può trovare quando un modulo è stato incluso prima nei moduli standard con questo incantesimo: perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo. Per File::Basenamequello era Perl 5.0.0, che è stato rilasciato alla fine degli anni '90, penso che sia ormai salva da usare.
Ha disegnato Stephens il

145

$ 0 è in genere il nome del tuo programma, quindi che ne dici?

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

Mi sembra che questo dovrebbe funzionare come abs_path sa se stai usando un percorso relativo o assoluto.

Aggiornamento Per chiunque legga questi anni dopo, dovresti leggere la risposta di Drew . È molto meglio del mio.


11
Piccolo commento, su activestate perl su Windows $ 0 in genere contiene barre rovesciate e abs_path restituite barre in avanti, quindi un rapido "tr / \ // \\ /;" era necessario per risolverlo.
Chris Madden,

3
volevo aggiungere che c'è un realpath, che è sinonimo di abs_path, nel caso in cui preferisci il nome senza sottolineatura
vol7ron,

@Chris, hai segnalato un bug al manutentore del modulo Cwd? Sembra un bug di adozione di Windows.
Znik

1
Altro problema che ho: perl -e 'use Cwd "abs_path";print abs_path($0);' stampe/tmp/-e
leonbloy,

2
@leonbloy Quando esegui uno script inline (con -e), credo che perl crei un file temporaneo per memorizzare lo script inline. Sembra che la posizione, nel tuo caso, sia /tmp. Cosa ti aspettavi che fosse il risultato?
GreenGiant


16

Penso che il modulo che stai cercando sia FindBin:

#!/usr/bin/perl
use FindBin;

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

11

Puoi usare FindBin , Cwd , File :: Basename o una loro combinazione. Sono tutti nella distribuzione di base di Perl IIRC.

Ho usato Cwd in passato:

Cwd:

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

@bmdhacks, hai ragione. La presunzione è che non hai cambiato 0 $. Ad esempio, lavori sopra appena l'avvio dello script (nel blocco di inizializzazione) o altrove quando non cambi $ 0. Ma $ 0 è un modo eccellente per cambiare la descrizione del processo visibile nello strumento unix 'ps' :) Questo può mostrare lo stato del processo corrente, ecc.
Dipende dallo

9

Ottenere il percorso assoluto verso $0o __FILE__è ciò che desideri. L'unico problema è se qualcuno ha fatto un chdir()ed $0era relativo - quindi è necessario ottenere il percorso assoluto in un BEGIN{}per evitare sorprese.

FindBincerca di fare di meglio e $PATHcercare di trovare qualcosa di simile abasename($0) , ma ci sono momenti in cui ciò fa cose troppo sorprendenti (in particolare: quando il file è "proprio di fronte a te" nel CWD.)

File::Fuha File::Fu->program_namee File::Fu->program_dirper questo.


È davvero probabile che qualcuno sia così sciocco da (in modo permanente) chdir()al momento della compilazione?
SamB,

Inizia semplicemente tutte le opere basate sulla directory corrente e $ 0 allo script.
Znik,

7

Qualche breve sfondo:

Sfortunatamente l'API Unix non fornisce un programma in esecuzione con il percorso completo dell'eseguibile. In effetti, il programma che esegue il tuo può fornire tutto ciò che vuole sul campo che normalmente dice al tuo programma di cosa si tratta. Esistono, come indicano tutte le risposte, varie euristiche per trovare probabili candidati. Ma a dir poco la ricerca nell'intero filesystem funzionerà sempre, e anche questo fallirà se l'eseguibile viene spostato o rimosso.

Ma non vuoi l'eseguibile Perl, che è ciò che è effettivamente in esecuzione, ma lo script che sta eseguendo. E Perl ha bisogno di sapere dove si trova la sceneggiatura. Memorizza questo file __FILE__, mentre $0proviene dall'API Unix. Questo può ancora essere un percorso relativo, quindi prendi il suggerimento di Mark e canonizzalo conFile::Spec->rel2abs( __FILE__ );


__FILE__mi dà ancora un percorso relativo. cioè ".".
Felwithe

6

Hai provato:

$ENV{'SCRIPT_NAME'}

o

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

Dipende davvero da come viene chiamato e se è CGI o eseguito da una shell normale, ecc.


$ ENV {'SCRIPT_NAME'} è vuoto quando lo script è in esecuzione sulla console
Putnik

Cattiva idea perché l'ambiente SCRIPT_NAME dipende dalla shell che si sta utilizzando. Questo è completamente incompatibile con Windows cmd.exe e incompatibile quando si chiama script direttamente da altri file binari. Non è prevista alcuna garanzia per questa variabile. I modi sopra sono molto più utilizzabili.
Znik,

6

Per ottenere il percorso della directory contenente il mio script ho usato una combinazione di risposte già fornite.

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

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

2

perlfaq8 risponde a una domanda molto simile usando la rel2abs()funzione on $0. Tale funzione è disponibile in File :: Spec.


2

Non è necessario utilizzare moduli esterni, con una sola riga puoi avere il nome del file e il relativo percorso. Se si utilizzano moduli e è necessario applicare un percorso relativo alla directory di script, il percorso relativo è sufficiente.

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

Non fornisce il percorso completo corretto dello script se lo si esegue come "./myscript.pl", come mostrerebbe solo "." anziché. Ma mi piace ancora questa soluzione.
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
Per favore, non rispondere solo con il codice. Spiega perché questa è la risposta corretta.
Lee Taylor,

1

Stai cercando questo ?:

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

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

L'output sarà simile al seguente:

You are running MyFileName.pl now.

Funziona su Windows e 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 

Mentre una risposta di sola fonte potrebbe risolvere la domanda dell'utente, non aiuta a capire perché funzioni. Hai dato all'utente un pesce, ma invece dovresti insegnargli come pescare.
Tin Man,

0

Il problema __FILE__è che stampa il percorso ".pm" del modulo principale non necessariamente il percorso di script ".cgi" o ".pl" in esecuzione. Immagino che dipenda dal tuo obiettivo.

Mi sembra che debba Cwdsolo essere aggiornato per mod_perl. Ecco il mio suggerimento:

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;

Si prega di aggiungere eventuali suggerimenti.


0

Senza moduli esterni, valido per la shell, funziona bene anche con '../':

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

test:

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

Cosa succede quando chiami symlink? Cwd funziona perfettamente con questo caso.
Znik,

0

Il problema con il solo utilizzo dirname(__FILE__)è che non segue i symlink. Ho dovuto usare questo per il mio script per seguire il link simbolico alla posizione effettiva del file.

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

0

Tutte le soluzioni senza librerie in realtà non funzionano per più di alcuni modi per scrivere un percorso (pensa ../o /bla/x/../bin/./x/../ ecc. La mia soluzione sembra di seguito. Ho una stranezza: non ho la più pallida idea del perché debba eseguire i rimpiazzi due volte. In caso contrario, ottengo un "./" o "../" falso. mi sembra abbastanza robusto.

  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

Nessuna delle risposte "migliori" era giusta per me. Il problema con l'utilizzo di FindBin '$ Bin' o Cwd è che restituiscono il percorso assoluto con tutti i collegamenti simbolici risolti. Nel mio caso avevo bisogno del percorso esatto con i collegamenti simbolici presenti - lo stesso che restituisce il comando Unix "pwd" e non "pwd -P". La seguente funzione fornisce la soluzione:

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

Su Windows usando dirnamee abs_pathinsieme ha funzionato meglio per me.

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

Ecco perché:

# 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

Cosa c'è che non va $^X?

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

Ti darebbe il percorso completo per il binario Perl in uso.

rovesciare


1
Fornisce il percorso al binario Perl mentre è richiesto il percorso di uno script
Putnik,

-5

Su * nix, probabilmente hai il comando "whereis", che cerca il tuo $ PATH alla ricerca di un binario con un determinato nome. Se $ 0 non contiene il nome completo del percorso, l'esecuzione di whereis $ scriptname e il salvataggio del risultato in una variabile dovrebbe indicare dove si trova lo script.


Non funzionerà, poiché $ 0 potrebbe anche restituire un percorso relativo al file: ../perl/test.pl
Lathan,

cosa succederà se lo script eseguibile non è in PATH?
Znik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.