it-swarm-eu.dev

Jak mohu získat úplnou cestu ke spuštění skriptu Perl?

Mám Perl skript a je třeba určit úplnou cestu a název souboru během provádění. Zjistil jsem, že v závislosti na tom, jak zavoláte skript $0 se liší a někdy obsahuje fullpath+filename a někdy jen filename. Protože se pracovní adresář může také lišit, nemůžu myslet na způsob, jak spolehlivě dostat fullpath+filename skriptu.

Má někdo řešení?

156
Chris Madden

Existuje několik způsobů:

  • $0 je aktuálně prováděný skript, jak je poskytován POSIX, relativně k aktuálnímu pracovnímu adresáři, pokud je skript na nebo pod CWD
  • Navíc cwd(), getcwd() a abs_path() jsou poskytovány modulem Cwd / a říkají, kde je skript spuštěn.
  • Modul FindBin poskytuje $Bin & $RealBin proměnné, které jsou obvykle cestou k vykonávajícímu skriptu; tento modul také poskytuje $Script & $RealScript, což je název skriptu
  • __FILE__ je skutečný soubor, který Perl interpret interpretuje během kompilace, včetně jeho plné cesty.

Viděl jsem první tři ( $0 , Cwd modul a FindBin module) selhává pod mod_Perl efektně, produkujíc bezcenný výstup jako '.' nebo prázdný řetězec. V takových prostředích používám __FILE__ a dostanu cestu z toho pomocí File::Basename module:

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

$ 0 je obvykle název vašeho programu, tak jak na tom?

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

Zdá se mi, že by to mělo fungovat, protože abs_path ví, zda používáte relativní nebo absolutní cestu.

Aktualizace Pro každého, kdo čte letos později, byste si měli přečíst Drewovu odpověď níže. Je to mnohem lepší než moje.

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

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

34
Mark

Myslím, že modul, který hledáte, je FindBin:

#!/usr/bin/Perl
use FindBin;

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

Můžete použít FindBin , Cwd , File :: Basename , nebo jejich kombinaci. Všechny jsou v základní distribuci Perl IIRC.

Použil jsem Cwd v minulosti:

Cwd:

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

Získání absolutní cesty k $0 nebo __FILE__ je to, co chcete. Jediný problém je, když někdo udělal chdir() a $0 byl relativní - pak musíte dostat absolutní cestu v BEGIN{}, aby se zabránilo jakémukoliv překvapení.

FindBin se snaží jít o něco lépe a hrabat se v $PATH pro něco, co odpovídá basename($0), ale jsou chvíle, kdy to dělá daleko-překvapující věci (konkrétně: když je soubor "přímo před vámi" v cwd.)

File::FuFile::Fu->program_name a File::Fu->program_dir pro toto.

9
Eric Wilhelm

Krátké pozadí:

Bohužel Unix API neposkytuje běžící program s úplnou cestou ke spustitelnému souboru. Ve skutečnosti, program vykonávající vaše může poskytnout, co chce, v poli, které obvykle říká váš program, co to je. Jak ukazují všechny odpovědi, existují různé heuristiky pro nalezení pravděpodobných kandidátů. Ale nic krátkého než hledání celého souborového systému bude vždy fungovat, a to i v případě, že bude spustitelný soubor přesunut nebo odstraněn, nezdaří.

Ale nechcete, aby spustitelný soubor Perl, který je ve skutečnosti spuštěn, ale skript, který se provádí. A Perl potřebuje vědět, kde je skript najít. Uloží to v __FILE__, zatímco $0 je z Unix API. To může být stále relativní cesta, takže berte Markův návrh a kanonizujte ho pomocí File::Spec->rel2abs( __FILE__ );

7
wnoise

Abych získal cestu k adresáři obsahujícímu můj skript, použil jsem kombinaci již daných odpovědí.

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

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

Zkusil jsi:

$ENV{'SCRIPT_NAME'}

nebo

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

To opravdu záleží na tom, jak se to nazývá, a zda je to CGI nebo běží z normálního Shell, atd.

6
Sean

perlfaq8 odpoví na velmi podobnou otázku pomocí funkce rel2abs() na $0. Tuto funkci naleznete v souboru :: Spec.

2
moritz

Není třeba používat externí moduly, pouze jeden řádek může mít název souboru a relativní cestu. Pokud používáte moduly a potřebujete použít cestu relativní k adresáři skriptů, je relativní cesta dostatečná.

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

Hledáte toto ?:

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

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

Výstup bude vypadat takto:

You are running MyFileName.pl now.

Funguje na Windows i Unixu.

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

Žádná z "top" odpovědí pro mě nebyla správná. Problém s použitím FindBin '$ Bin' nebo Cwd je, že vrací absolutní cestu se všemi symbolickými odkazy vyřešenými. V mém případě jsem potřeboval přesnou cestu se symbolickými odkazy - stejně jako vrací příkaz Unix "pwd" a ne "pwd -P". Řešení poskytuje následující funkce:

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

Problém s pouhým použitím dirname(__FILE__) je, že nesleduje symbolické odkazy. Musel jsem to použít pro můj skript sledovat symbolický odkaz na aktuální umístění souboru.

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

Problém s __FILE__ je, že vytiskne cestu ".pm" jádra modulu ne nutně ".cgi" nebo ".pl" skript cestu, která je spuštěna. Myslím, že záleží na tom, co je vaším cílem.

Zdá se mi, že Cwd prostě musí být aktualizován pro mod_Perl. Zde je můj návrh:

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;

Přidejte prosím jakékoli návrhy.

0
Jonathan

Bez jakýchkoli externích modulů, platných pro Shell, funguje dobře i s '../':

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

Všechna řešení bez knihoven ve skutečnosti nefungují více než několika způsoby, jak napsat cestu (myslím ../ nebo /bla/x/../bin/./x/../ atd. Moje řešení vypadá jako Mám jeden vtípek: Nemám nejslabší představu, proč musím dvakrát vyměnit, pokud ne, dostanu falešný "./" nebo "../". Zdá se mi to dost robustní.

  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