it-swarm-eu.dev

Wie erhalte ich den vollständigen Pfad zu einem ausgeführten Perl-Skript?

Ich habe ein Perl-Skript und muss den vollständigen Pfad und Dateinamen des Skripts während der Ausführung ermitteln. Ich habe festgestellt, dass $0 je nach Aufruf des Skripts variiert und manchmal den fullpath+filename und manchmal nur filename enthält. Da das Arbeitsverzeichnis auch variieren kann, fällt mir kein Weg ein, den fullpath+filename des Skripts zuverlässig abzurufen.

Hat jemand eine Lösung?

156
Chris Madden

Es gibt einige Möglichkeiten:

  • $0 ist das aktuell ausgeführte Skript, wie von POSIX bereitgestellt, relativ zum aktuellen Arbeitsverzeichnis, wenn sich das Skript auf oder unter der CWD befindet
  • Zusätzlich werden cwd(), getcwd() und abs_path() vom Modul Cwd bereitgestellt und sagen Ihnen, von wo aus das Skript ausgeführt wird
  • Das Modul FindBin enthält die $Bin- und $RealBin-Variablen, die normalerweise der Pfad zum ausgeführten Skript sind. Dieses Modul enthält auch $Script & $RealScript, die den Namen des Skripts darstellen
  • __FILE__ ist die Datei, die der Perl-Interpreter während der Kompilierung behandelt, einschließlich des vollständigen Pfads.

Ich habe gesehen, wie die ersten drei ( $0 , das Cwd -Modul und das FindBin modul) unter mod_Perl spektakulär ausfallen und dabei wertlose Ausgaben wie '.' oder einen leeren String erzeugen. In solchen Umgebungen verwende ich __FILE__ und erhalte den Pfad davon mit dem Modul File::Basename :

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

$ 0 ist normalerweise der Name Ihres Programms. Wie wäre es damit?

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

Es scheint mir, dass dies funktionieren sollte, da abs_path weiß, ob Sie einen relativen oder absoluten Pfad verwenden.

Update Für alle, die dies Jahre später lesen, sollten Sie die Antwort von Drew unten lesen. Es ist viel besser als meiner.

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

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

34
Mark

Ich denke, das Modul, nach dem Sie suchen, ist FindBin:

#!/usr/bin/Perl
use FindBin;

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

Sie könnten FindBin , Cwd , File :: Basename oder eine Kombination davon verwenden. Sie befinden sich alle in der Basisdistribution von Perl IIRC.

Ich habe in der Vergangenheit Cwd verwendet:

Cwd:

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

Sie möchten den absoluten Pfad nach $0 oder __FILE__ bekommen. Das einzige Problem ist, wenn jemand eine chdir() gemacht hat und der $0 relativ war - dann müssen Sie den absoluten Pfad in einem BEGIN{} bekommen, um Überraschungen zu vermeiden.

FindBin versucht, im $PATH etwas besser zu suchen, um etwas zu finden, das mit der basename($0) übereinstimmt, aber manchmal tut das viel zu überraschende Dinge (speziell: wenn die Datei in der cwd "direkt vor Ihnen" ist.)

File::Fu hat hierfür File::Fu->program_name und File::Fu->program_dir.

9
Eric Wilhelm

Einige kurze Hintergründe:

Leider stellt die Unix-API kein laufendes Programm mit dem vollständigen Pfad zur ausführbaren Datei zur Verfügung. In der Tat kann das Programm, das Ihr ausführt, alles geben, was es in dem Feld wünscht, das Ihrem Programm normalerweise sagt, was es ist. Wie alle Antworten zeigen, gibt es verschiedene Heuristiken, um mögliche Kandidaten zu finden. Aber nichts anderes als das Durchsuchen des gesamten Dateisystems wird immer funktionieren, und selbst das wird fehlschlagen, wenn die ausführbare Datei verschoben oder entfernt wird.

Sie möchten jedoch nicht die ausführbare Perl-Datei, sondern das Skript, das gerade ausgeführt wird. Und Perl muss wissen, wo sich das Skript befindet, um es zu finden. Es speichert dies in __FILE__, während $0 von der Unix-API stammt. Dies kann immer noch ein relativer Pfad sein. Nehmen Sie den Vorschlag von Markus und machen Sie ihn mit File::Spec->rel2abs( __FILE__ ); heilig.

7
wnoise

Um den Pfad zu dem Verzeichnis mit meinem Skript zu erhalten, habe ich bereits eine Kombination von Antworten verwendet.

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

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

Hast du es versucht:

$ENV{'SCRIPT_NAME'}

oder

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

Es hängt wirklich davon ab, wie es aufgerufen wird und ob es sich um CGI handelt oder von einer normalen Shell ausgeführt wird usw.

6
Sean

perlfaq8 beantwortet eine sehr ähnliche Frage mit der Funktion rel2abs() für $0. Diese Funktion befindet sich in File :: Spec.

2
moritz

Es müssen keine externen Module verwendet werden. Mit nur einer Zeile können Sie den Dateinamen und den relativen Pfad angeben. Wenn Sie Module verwenden und einen Pfad relativ zum Skriptverzeichnis anwenden müssen, reicht der relative Pfad aus.

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

Suchen Sie das ?:

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

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

Die Ausgabe sieht folgendermaßen aus:

You are running MyFileName.pl now.

Es funktioniert sowohl unter Windows als auch unter 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

Keine der "besten" Antworten war für mich richtig. Das Problem bei der Verwendung von FindBin '$ Bin' oder Cwd ist, dass sie einen absoluten Pfad mit allen aufgelösten symbolischen Links zurückgeben. In meinem Fall brauchte ich den exakten Pfad mit vorhandenen symbolischen Links - genauso wie der Unix-Befehl "pwd" und nicht "pwd -P". Die folgende Funktion bietet die Lösung:

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

Das Problem bei der Verwendung von dirname(__FILE__) ist, dass es nicht auf symbolische Links folgt. Ich musste dies für mein Skript verwenden, um dem symbolischen Link zum tatsächlichen Dateispeicherort zu folgen.

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

Das Problem mit __FILE__ ist, dass der ".pm" -Pfad des Kernmoduls gedruckt wird, nicht unbedingt der Skriptpfad ".cgi" oder ".pl", der ausgeführt wird. Ich denke, es hängt davon ab, was dein Ziel ist.

Mir scheint, dass Cwd nur für mod_Perl aktualisiert werden muss. Hier ist mein Vorschlag:

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;

Bitte fügen Sie Vorschläge hinzu.

0
Jonathan

Ohne externe Module, die für Shell gültig sind, funktionieren sie auch mit '../' gut:

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

prüfung:

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

Alle bibliotheksfreien Lösungen funktionieren eigentlich nicht für mehr als ein paar Möglichkeiten, um einen Pfad zu schreiben (think ../ oder /bla/x/../bin/./x/../ usw.). Meine Lösung sieht so aus Ich habe eine Eigenart: Ich habe nicht die geringste Ahnung, warum ich die Auswechslungen zweimal ausführen muss. Wenn nicht, bekomme ich ein falsches "./" oder "../". Abgesehen davon erscheint mir ziemlich robust.

  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