it-swarm.it

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

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

Qualcuno ha una soluzione?

156
Chris Madden

Ci sono alcuni modi:

  • $0 è lo script attualmente in esecuzione come fornito da POSIX, relativo alla directory di lavoro corrente se lo script è uguale o inferiore al CWD
  • Inoltre, cwd(), getcwd() e abs_path() sono forniti dal modulo Cwd e ti dicono da dove viene eseguito lo script
  • Il modulo FindBin fornisce le variabili $Bin & $RealBin che solitamente sono il percorso dello script in esecuzione; questo modulo fornisce anche $Script & $RealScript che sono il nome dello script
  • __FILE__ è il file effettivo che l'interprete Perl gestisce durante la compilazione, compreso il suo percorso completo.

Ho visto i primi tre ( $0 , il Cwd modulo e il FindBin module) falliscono sotto mod_Perl in modo spettacolare, producendo output senza valore come '.' o un vuoto stringa. In tali ambienti, utilizzo __FILE__ e ottengo il percorso da quello utilizzando il modulo File::Basename :

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

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

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 coloro che leggono questi anni dopo, dovresti leggere la risposta di Drew sotto. È molto meglio del mio.

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

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

34
Mark

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

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

Ho usato Cwd in passato:

Cwd:

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

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

FindBin prova ad andare meglio e ad aggirarsi nel $PATH per qualcosa che corrisponde al basename($0), ma ci sono momenti in cui ciò fa cose troppo sorprendenti (in particolare: quando il file è "proprio di fronte a te" nella cwd).

File::Fu ha File::Fu->program_name e File::Fu->program_dir per questo.

9
Eric Wilhelm

Qualche breve background:

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 nel campo che normalmente dice al tuo programma di cosa si tratta. Vi sono, come sottolineano tutte le risposte, varie euristiche per trovare candidati probabili. Ma qualsiasi ricerca nell'intero filesystem funzionerà sempre, e anche quella fallirà se l'eseguibile viene spostato o rimosso.

Ma non vuoi che l'eseguibile Perl, che è ciò che è in realtà in esecuzione, ma lo script che sta eseguendo. E Perl deve sapere dove lo script deve trovarlo. Memorizza questo in __FILE__, mentre $0 proviene dall'API Unix. Questo può ancora essere un percorso relativo, quindi prendi il suggerimento di Marco e canonizzalo con File::Spec->rel2abs( __FILE__ );

7
wnoise

Per ottenere il percorso verso la directory che contiene il mio script ho usato già una combinazione di risposte fornite.

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

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

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 viene eseguito da una Shell normale, ecc.

6
Sean

perlfaq8 risponde a una domanda molto simile usando la funzione rel2abs() su $0. Questa funzione può essere trovata in File :: Spec.

2
moritz

Non è necessario utilizzare moduli esterni, con una sola riga è possibile avere il nome del file e il percorso relativo. Se stai usando i moduli e hai bisogno di applicare un percorso relativo alla directory degli script, il percorso relativo è sufficiente.

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

Stai cercando questo ?:

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

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

L'output sarà simile a questo:

You are running MyFileName.pl now.

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

Nessuna delle risposte "migliori" era giusta per me. Il problema con l'uso 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 link 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
drjumper

Il problema con l'uso di dirname(__FILE__) è che non segue i collegamenti simbolici. Ho dovuto usare questo per il mio script per seguire il link simbolico alla posizione del file reale.

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

Il problema con __FILE__ è che stamperà il percorso ".pm" del modulo principale non necessariamente il percorso di script ".cgi" o ".pl" in esecuzione. Immagino dipenda da quale sia il tuo obiettivo.

Mi sembra che Cwd abbia solo bisogno di 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
Jonathan

Senza alcun modulo esterno, valido per 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
0
Putnik

Tutte le soluzioni libere da libreria non funzionano più di alcuni modi per scrivere un percorso (si pensi ../o /bla/x/../bin/./x/../ ecc. La mia soluzione sembra Di seguito, ho una stranezza: non ho la minima idea del motivo per cui devo eseguire le sostituzioni due volte. Se non lo faccio, ottengo uno spurio "./" o "../". A parte questo, sembra abbastanza robusto per me.

  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