it-swarm-id.com

Bagaimana cara mendapatkan path lengkap ke skrip Perl yang sedang dieksekusi?

Saya memiliki skrip Perl dan perlu menentukan path lengkap dan nama file skrip selama eksekusi. Saya menemukan bahwa tergantung pada bagaimana Anda memanggil skrip $0 bervariasi dan terkadang berisi fullpath+filename dan terkadang hanya filename. Karena direktori yang berfungsi dapat bervariasi juga, saya tidak dapat memikirkan cara untuk mendapatkan fullpath+filename script secara andal.

Adakah yang punya solusi?

156
Chris Madden

$ 0 biasanya nama program Anda, jadi bagaimana dengan ini?

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

Menurut saya ini seharusnya berfungsi karena abs_path tahu jika Anda menggunakan jalur relatif atau absolut.

Perbarui Bagi siapa pun yang membaca ini tahun kemudian, Anda harus membaca jawaban Drew di bawah ini. Ini jauh lebih baik daripada milikku.

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

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

34
Mark

Saya pikir modul yang Anda cari adalah FindBin:

#!/usr/bin/Perl
use FindBin;

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

Anda dapat menggunakan FindBin , Cwd , File :: Basename , atau kombinasi keduanya. Semuanya ada di basis distribusi Perl IIRC.

Saya menggunakan Cwd di masa lalu:

Cwd:

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

Mendapatkan jalur absolut ke $0 atau __FILE__ adalah yang Anda inginkan. Satu-satunya masalah adalah jika seseorang melakukan chdir() dan $0 relatif - maka Anda perlu mendapatkan path absolut dalam BEGIN{} untuk mencegah kejutan.

FindBin mencoba melakukan yang lebih baik dan merendahkan diri di $PATH untuk sesuatu yang cocok dengan basename($0), tetapi ada kalanya hal itu melakukan hal yang terlalu mengejutkan (khususnya: ketika file "tepat di depan Anda" di cwd.)

File::Fu memiliki File::Fu->program_name dan File::Fu->program_dir untuk ini.

9
Eric Wilhelm

Beberapa latar belakang singkat:

Sayangnya Unix API tidak menyediakan program yang berjalan dengan jalur lengkap ke yang dapat dieksekusi. Bahkan, program yang menjalankan program Anda dapat memberikan apa pun yang diinginkan di bidang yang biasanya memberi tahu program Anda tentang apa itu. Ada, seperti semua jawaban tunjukkan, berbagai heuristik untuk menemukan calon yang mungkin. Tetapi tidak ada kekurangan mencari seluruh sistem file akan selalu bekerja, dan bahkan itu akan gagal jika executable dipindahkan atau dihapus.

Tetapi Anda tidak ingin Perl dapat dieksekusi, yang sebenarnya sedang berjalan, tetapi skrip yang dieksekusi. Dan Perl perlu tahu di mana skrip itu menemukannya. Ini menyimpan ini dalam __FILE__, sedangkan $0 berasal dari Unix API. Ini masih bisa menjadi jalur relatif, jadi terima saran Mark dan dikanonkan dengan File::Spec->rel2abs( __FILE__ );

7
wnoise

Untuk mendapatkan path ke direktori yang berisi skrip saya, saya sudah menggunakan kombinasi jawaban yang sudah diberikan.

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

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

Sudahkah Anda mencoba:

$ENV{'SCRIPT_NAME'}

atau

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

Itu benar-benar tergantung pada bagaimana itu dipanggil dan apakah itu CGI atau dijalankan dari Shell normal, dll.

6
Sean

perlfaq8 menjawab pertanyaan yang sangat mirip dengan menggunakan fungsi rel2abs() pada $0 Fungsi itu dapat ditemukan di File :: Spec.

2
moritz

Tidak perlu menggunakan modul eksternal, hanya dengan satu baris Anda dapat memiliki nama file dan jalur relatif. Jika Anda menggunakan modul dan perlu menerapkan jalur relatif ke direktori skrip, jalur relatif sudah cukup.

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

Apakah Anda mencari ini ?:

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

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

Outputnya akan terlihat seperti ini:

You are running MyFileName.pl now.

Ini bekerja pada Windows dan 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

Tidak ada jawaban "atas" yang tepat untuk saya. Masalah dengan menggunakan FindBin '$ Bin' atau Cwd adalah mereka mengembalikan path absolut dengan semua tautan simbolik diselesaikan. Dalam kasus saya, saya membutuhkan jalur yang tepat dengan tautan simbolik yang ada - sama seperti mengembalikan perintah Unix "pwd" dan bukan "pwd -P". Fungsi berikut menyediakan solusinya:

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

Masalah dengan hanya menggunakan dirname(__FILE__) adalah tidak mengikuti symlinks. Saya harus menggunakan ini untuk skrip saya untuk mengikuti symlink ke lokasi file yang sebenarnya.

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

Masalah dengan __FILE__ adalah bahwa ia akan mencetak jalur modul inti ".pm" belum tentu jalur skrip ".cgi" atau ".pl" yang sedang berjalan. Saya kira itu tergantung pada apa tujuan Anda.

Sepertinya saya bahwa Cwd hanya perlu diperbarui untuk mod_Perl. Ini saran saya:

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;

Silakan tambahkan saran.

0
Jonathan

Tanpa modul eksternal apa pun, berlaku untuk Shell, berfungsi dengan baik bahkan dengan '../':

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

uji:

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

Semua solusi bebas pustaka tidak benar-benar berfungsi lebih dari beberapa cara untuk menulis lintasan (pikirkan ../ atau /bla/x/../bin/./x/../ dll. Solusi saya terlihat seperti di bawah ini. Saya punya satu kekhasan: Saya tidak punya ide sedikit pun mengapa saya harus menjalankan penggantian dua kali. Jika tidak, saya mendapatkan palsu "./" atau "../". Selain itu, itu sepertinya cukup kuat untukku.

  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