web-dev-qa-db-fra.com

Comment obtenir le chemin d'accès complet à un script Perl en cours d'exécution?

J'ai un script Perl et j'ai besoin de déterminer le chemin d'accès complet et le nom du fichier du script pendant son exécution. J'ai découvert que, selon la manière dont vous appelez le script, $0 varie et contient parfois le fullpath+filename et parfois juste filename. Comme le répertoire de travail peut également varier, je ne trouve pas de moyen d'obtenir de manière fiable le fullpath+filename du script.

Quelqu'un a une solution?

156
Chris Madden

Il y a plusieurs façons:

  • $0 est le script en cours d'exécution tel que fourni par POSIX, par rapport au répertoire de travail en cours si le script est égal ou inférieur à CWD
  • De plus, cwd(), getcwd() et abs_path() sont fournis par le module Cwd et vous indiquent d'où le script est exécuté.
  • Le module FindBin fournit les variables $Bin & $RealBin qui habituellement sont le chemin d'accès au script en cours d'exécution; ce module fournit également $Script & $RealScript qui sont le nom du script
  • __FILE__ est le fichier utilisé par l'interpréteur Perl lors de la compilation, y compris son chemin d'accès complet.

J'ai vu les trois premiers ( $0 , le Cwd module et le FindBin module) échouer de manière spectaculaire sous mod_Perl, produisant des résultats sans valeur tels que '.' ou une chaîne vide. Dans de tels environnements, j'utilise __FILE__ et j'obtiens le chemin à l'aide du module File::Basename :

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

$ 0 est généralement le nom de votre programme, alors, qu'en est-il?

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

Il me semble que cela devrait fonctionner comme abs_path sait si vous utilisez un chemin relatif ou absolu.

Mise à jour Si vous lisez ceci quelques années plus tard, vous devriez lire la réponse de Drew ci-dessous. C'est beaucoup mieux que le mien.

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

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

34
Mark

Je pense que le module que vous recherchez est FindBin:

#!/usr/bin/Perl
use FindBin;

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

Vous pouvez utiliser FindBin , Cwd , File :: Basename ou une combinaison de ceux-ci. Ils sont tous dans la distribution de base de Perl IIRC.

J'ai utilisé Cwd dans le passé:

Cwd:

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

Obtenir le chemin absolu vers $0 ou __FILE__ est ce que vous voulez. Le seul problème est que si quelqu'un a utilisé chdir() et que $0 est relatif - vous devez alors définir le chemin absolu dans un BEGIN{} pour éviter toute surprise.

FindBin essaie de faire mieux et de ramper dans le $PATH pour quelque chose qui correspond au basename($0), mais il y a des moments où cela fait des choses bien trop surprenantes (en particulier: lorsque le fichier est "juste en face de vous" dans le dossier).

File::Fu a File::Fu->program_name et File::Fu->program_dir pour cela.

9
Eric Wilhelm

Quelques arrière-plans:

Malheureusement, l'API Unix ne fournit pas à un programme en cours d'exécution le chemin d'accès complet à l'exécutable. En fait, le programme qui exécute le vôtre peut fournir ce qu'il veut sur le terrain, qui indique normalement à votre programme ce que c'est. Comme toutes les réponses le soulignent, il existe diverses heuristiques pour trouver des candidats probables. Mais rien de moins que de rechercher le système de fichiers entier ne fonctionnera toujours, et même cela échouera si l'exécutable est déplacé ou supprimé.

Mais vous ne voulez pas que l'exécutable Perl, qui est ce qui est réellement en cours d'exécution, mais le script qu'il exécute. Et Perl a besoin de savoir où se trouve le script pour le trouver. Il stocke cela dans __FILE__, alors que $0 provient de l'API Unix. Cela peut toujours être un chemin relatif, alors prenez la suggestion de Mark et canonisez-la avec File::Spec->rel2abs( __FILE__ );

7
wnoise

Afin d’obtenir le chemin du répertoire contenant mon script, j’ai déjà utilisé une combinaison de réponses.

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

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

As-tu essayé:

$ENV{'SCRIPT_NAME'}

ou

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

Cela dépend vraiment de la façon dont on l'appelle et de la manière dont il s'agit de CGI ou de l'exécution d'un shell normal, etc.

6
Sean

perlfaq8 répond à une question très similaire en utilisant la fonction rel2abs() sur $0. Cette fonction peut être trouvée dans File :: Spec.

2
moritz

Il n'est pas nécessaire d'utiliser des modules externes, avec une seule ligne, vous pouvez avoir le nom du fichier et le chemin relatif. Si vous utilisez des modules et devez appliquer un chemin relatif au répertoire de script, le chemin relatif est suffisant.

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

Vous cherchez cela?:

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

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

La sortie ressemblera à ceci:

You are running MyFileName.pl now.

Cela fonctionne sur Windows et 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

Aucune des réponses "top" ne me convenait. Le problème avec FindBin '$ Bin' ou Cwd est qu’ils renvoient un chemin absolu avec tous les liens symboliques résolus. Dans mon cas, j'avais besoin du chemin exact avec les liens symboliques présents - identique à celui qui renvoie la commande Unix "pwd" et non "pwd -P". La fonction suivante fournit la solution:

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

Le seul problème avec dirname(__FILE__) est qu’il ne suit pas les liens symboliques. Je devais l'utiliser pour que mon script suive le lien symbolique vers l'emplacement actuel du fichier.

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

Le problème avec __FILE__ est qu’il imprimera le chemin du module principal ".pm" pas nécessairement le chemin de script ".cgi" ou ".pl" en cours d’exécution. Je suppose que cela dépend de votre objectif.

Il me semble que Cwd doit simplement être mis à jour pour mod_Perl. Voici ma suggestion:

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;

S'il vous plaît ajouter des suggestions.

0
Jonathan

Sans aucun module externe, valable pour Shell, fonctionne bien même avec '../':

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

tester:

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

Toutes les solutions sans bibliothèque ne fonctionnent réellement que pour plusieurs façons d'écrire un chemin (pensez à ../ ou /bla/x/../bin/./x/../ etc. Ma solution ressemble à ceci: J'ai un problème: je n'ai pas la moindre idée de la raison pour laquelle je dois exécuter les remplacements deux fois. Si je ne le fais pas, je reçois un faux "./" ou "../". semble assez robuste pour moi.

  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