Yürütülen bir Perl betiğinin tam yolunu nasıl edinebilirim?


168

Perl komut dosyası var ve yürütme sırasında komut dosyasının tam yolunu ve dosya adını belirlemeniz gerekir. Ne senaryoyu çağrı şekline bağlı olduğunu keşfetti $0içeren bazen değişir ve fullpath+filenamebazen sadece filename. Çalışma dizini de değişebileceğinden fullpath+filename, komut dosyasını güvenilir bir şekilde almanın bir yolunu düşünemiyorum .

Birinin çözümü olan var mı?

Yanıtlar:


251

Birkaç yol vardır:

  • $0 komut dosyası CWD'de veya altındaysa, geçerli çalışma dizinine göre POSIX tarafından sağlanan yürütme komut dosyasıdır
  • Ayrıca, cwd(), getcwd()ve abs_path()tarafından sağlanmaktadır Cwdmodülü ve komut çalıştırılıyor nerede söylemek
  • Modül FindBin, genellikle yürütme komut dosyasının yolu olan $Bin& $RealBindeğişkenlerini sağlar ; Bu modül ayrıca sağlar ve bu komut dosyasının adı verilebilir$Script$RealScript
  • __FILE__ Perl yorumlayıcısının derleme sırasında tam yolu da dahil olmak üzere ele aldığı gerçek dosyadır.

Ben ilk üç ( $0, Cwdmodül ve FindBinmodül) mod_perlmuhteşem altında başarısız gördüm, '.'ya da boş bir dize gibi değersiz çıktı üreten . Bu tür ortamlarda, modülü __FILE__kullanarak bunu kullanıyorum ve yolunu alıyorum File::Basename:

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

2
Bu gerçekten en iyi çözümdür, özellikle de halihazırda değiştirilmiş bir $
0'ınız varsa

8
Görünüşe göre abs_path, sadece yolu içeren bir isim içerebileceğinden _____FILE_____ ile kullanılmalıdır.
Artçı Şok

6
@vicTROLLA Muhtemelen bu yanıtın en büyük önerisi (dirname ile birlikte __FILE__) beklendiği gibi çalışmıyor mu? Kabul edilen cevap bana tam mutlak yolu verirken, komut dosyasının yürütüldüğü yerden göreceli yol ile sonuçlanır.
Izkata

10
dirname(__FILE__)sembolik bağlantıları izlemez, bu nedenle yürütülebilir dosyayı bağladıysanız ve yükleme konumunda başka bir dosyanın konumunu bulmayı umuyorsanız, kontrol etmeniz if( -l __FILE__)ve ardından kontrol etmeniz gerekir dirname(readlink(__FILE__)).
DavidG

3
Bir modül öncelikle bu sözler ile standart modüllere dahil edildiğinde @IliaRostovtsev Şunları bulabilirsiniz: perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo. Çünkü File::Basename90'lı yılların sonlarında piyasaya sürülen Perl 5.0.0'dı.
Drew Stephens

145

$ 0 genellikle programınızın adıdır, peki buna ne dersiniz?

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

Bana göreceli olarak bu göreceli veya mutlak bir yol kullanıp kullanmadığınızı bilir.

Güncelleme Bu yıllar sonra okuyan herkes için Drew'un cevabını okumalısınız . Benimkinden çok daha iyi.


11
Küçük bir yorum, Windows 0'daki activestate perl'de genellikle ters eğik çizgiler ve abs_path ileri eğik çizgiler içeriyor, bu nedenle hızlı bir "tr / \ // \\ /;" düzeltmek gerekiyordu.
Chris Madden

3
altı çizili adı tercih ederseniz, realpatheşanlamlı bir a olduğunu eklemek istedimabs_path
vol7ron

@Chris, hatayı Cwd modül sürdürücüsüne bildirdin mi? Windows benimseme hatası gibi görünüyor.
Znik

1
Diğer sorunum var: perl -e 'use Cwd "abs_path";print abs_path($0);' baskılar/tmp/-e
leonbloy

2
@leonbloy Bir komut satırını (ile -e) yürüttüğünüzde, perl'in satır içi komut dosyanızı depolamak için bir geçici dosya oluşturduğuna inanıyorum. Konum, sizin durumunuzda olduğu gibi görünüyor /tmp. Sonucun ne olmasını beklediniz?
GreenGiant


16

Bence aradığınız modül FindBin:

#!/usr/bin/perl
use FindBin;

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

11

Sen kullanabilirsiniz FindBin , CWD , Dosya :: BASENAME veya bunların bir kombinasyonu. Hepsi Perl IIRC'nin temel dağıtımında.

Geçmişte Cwd kullandım:

cwd:

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

@bmdhacks, haklısın. Varsayım, 0 $ değiştirmediniz. Örneğin, komut dosyası başlar başlamaz (başlatma bloğunda) yukarıda veya $ 0'ı değiştirmediğiniz başka bir yerde çalışırsınız. Ancak $ 0, 'ps' unix aracı altında görünen işlem açıklamasını değiştirmek için mükemmel bir yoldur :) Bu, para birimi işlem durumunu vb.
Gösterebilir

9

İstediğinize $0veya ona giden mutlak yolu elde __FILE__etmek. Tek sorun birisi yaptı chdir()ve $0göreceli - o zaman BEGIN{}herhangi bir sürpriz önlemek için bir mutlak yol almak gerekir .

FindBinbir daha iyi gitmeye çalışır ve $PATHeşleşen bir şey için etrafında grovel basename($0), ama bunun çok şaşırtıcı şeyler yaptığı zamanlar vardır (özellikle: dosya cwd "önünüzde" olduğunda.)

File::Fusahiptir File::Fu->program_nameve File::Fu->program_dirbunun için.


Herhangi birinin chdir()derleme zamanında (kalıcı olarak) bu kadar aptal olması gerçekten olası mı ?
SamB

Sadece geçerli dir dayalı tüm işleri yapmak ve script 0 $ başlar.
Znik

7

Kısa bir arka plan:

Ne yazık ki Unix API, çalıştırılabilir programın tam yolunu içeren çalışan bir program sağlamaz. Aslında, kendi programınızı yürüten program, normalde programınıza ne olduğunu söyleyen alanda istediği her şeyi sağlayabilir. Tüm cevapların işaret ettiği gibi, olası adayları bulmak için çeşitli sezgisel tarama vardır. Ancak, tüm dosya sistemini aramaktan hiçbir şey her zaman işe yaramaz ve yürütülebilir dosya taşındığında veya kaldırıldığında bile başarısız olur.

Ancak Perl çalıştırılabilir olmasını istemiyorsunuz, ki bu gerçekten çalışıyor, ama çalıştırdığı komut dosyası. Perl'in senaryoyu nerede bulacağını bilmesi gerekiyor. Bu in depolar __FILE__iken, $0Unix API değil. Bu hala göreceli bir yol olabilir, bu yüzden Mark'ın önerisini alın veFile::Spec->rel2abs( __FILE__ );


__FILE__hala bana göreceli bir yol veriyor. yani '.'.
16:46, felwithe

6

Denedin mi:

$ENV{'SCRIPT_NAME'}

veya

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

Gerçekten nasıl çağrıldığına ve CGI mı yoksa normal bir kabuktan mı çalıştırıldığına bağlıdır.


Komut dosyası konsolda çalışırken $ ENV {'SCRIPT_NAME'} boş
Putnik

SCRIPT_NAME ortamı kullandığınız kabuğa bağlı olduğu için kötü fikir. Bu, windows cmd.exe ile tam uyumsuzdur ve komut dosyasını doğrudan diğer ikili dosyalardan çağırdığınızda uyumsuzdur. Bu wariable'ın ayarlanmış bir garantisi yoktur. Yukarıdaki yollar çok daha kullanılabilir.
Znik

6

Senaryomun bulunduğu dizinin yolunu bulmak için verilen cevapların bir kombinasyonunu kullandım.

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

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

2

perlfaq8 , rel2abs()fonksiyonu kullanarak çok benzer bir soruyu cevaplar $0. Bu işlev File :: Spec.


2

Harici modüller kullanmaya gerek yoktur, sadece bir satırla dosya adına ve göreli yola sahip olabilirsiniz. Modüller kullanıyorsanız ve komut dosyası dizinine göre bir yol uygulamanız gerekiyorsa, göreli yol yeterlidir.

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

Komut dosyasının "./myscript.pl" gibi çalıştırırsanız, komut dosyasının tam yolunu sağlamaz, çünkü yalnızca "." yerine. Ama yine de bu çözümü seviyorum.
Keve

1
#!/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


4
Lütfen sadece kodla cevap vermeyin. Lütfen bunun neden doğru cevap olduğunu açıklayın.
Lee Taylor

1

Bunu mu arıyorsunuz ?:

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

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

Çıktı şöyle görünecektir:

You are running MyFileName.pl now.

Hem Windows hem de Unix üzerinde çalışır.


0
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 

Yalnızca kaynak yanıtı, kullanıcının sorusunu çözse de, neden çalıştığını anlamalarına yardımcı olmaz. Kullanıcıya bir balık verdiniz, ancak bunun yerine onlara balık tutmayı NASIL öğretmelisiniz.
Tin Man

0

Sorun __FILE__, çekirdek modül ".pm" yolunu mutlaka çalışan ".cgi" veya ".pl" komut dosyası yolunu yazdırmasıdır. Sanırım hedefin ne olduğuna bağlı.

Bana öyle geliyor ki Cwdsadece mod_perl için güncellenmesi gerekiyor. İşte benim önerim:

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;

Lütfen herhangi bir öneri ekleyin.


0

Kabuk için geçerli herhangi bir harici modül olmadan, '../' ile bile iyi çalışır:

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

Ölçek:

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

Symlink'i aradığınızda ne olacak? Cwd bu durumda mükemmel çalışıyor.
Znik

0

Sadece kullanmayla ilgili sorun, dirname(__FILE__)sembolik bağlantıları takip etmemesidir. Ben gerçek dosya konuma symlink izlemek için benim komut dosyası kullanmak zorunda kaldı.

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

0

Tüm kütüphane içermeyen çözümler bir yol yazmak için birkaç yoldan fazla işe yaramaz (düşünün ../ veya /bla/x/../bin/./x/../ vs. Bir tuhaflığım var: Değiştirmeleri neden iki kez çalıştırmak zorunda olduğum hakkında en ufak bir fikrim yok.Yoksa, sahte bir "./" veya "../" alıyorum. bana oldukça sağlam geliyor.

  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

"En iyi" yanıtların hiçbiri benim için doğru değildi. FindBin '$ Bin' veya Cwd kullanmayla ilgili sorun, tüm sembolik bağlantılar çözülmüş olarak mutlak yol döndürmeleridir. Benim durumumda sembolik linkler olan tam yola ihtiyacım vardı - "pwd -P" değil, Unix komutu "pwd" ile aynı. Aşağıdaki işlev çözüm sağlar:

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

Windows üzerinde dirnameve abs_pathbirlikte kullanmak benim için en iyi çalıştı.

use File::Basename;
use Cwd qw(abs_path);

# absolute path of the directory containing the executing script
my $abs_dirname = dirname(abs_path($0));
print "\ndirname(abs_path(\$0)) -> $abs_dirname\n";

İşte nedeni:

# this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__); 
print "dirname(__FILE__) -> $rel_dirname\n"; 

# this gives the slightly wrong answer, but in the form I want 
my $full_filepath = abs_path($0);
print "abs_path(\$0) -> $full_filepath\n";

-2

Sorun nedir $^X?

#!/usr/bin/env perl<br>
print "This is executed by $^X\n";

Kullanılan Perl ikilisinin tam yolunu verir.

tersyüz etmek


1
Perl ikilisine yol verirken gerekli bir senaryoya yol verir
Putnik

-5

* Nix'de, büyük olasılıkla $ PATH değerinde belirli bir ada sahip bir ikili dosya arayan "whereis" komutuna sahipsiniz. $ 0 tam yol adını içermiyorsa, whereis $ komut dosyası adını çalıştırmak ve sonucu bir değişkene kaydetmek, komut dosyasının nerede olduğunu size bildirmelidir.


$ 0 da dosyaya göreceli bir yol döndürebildiğinden, bu işe yaramaz: ../perl/test.pl
Lathan

yürütülebilir komut dosyası PATH dışındaysa ne olur?
Znik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.