it-swarm-vi.tech

Làm cách nào để có được đường dẫn đầy đủ đến tập lệnh Perl đang thực thi?

Tôi có tập lệnh Perl và cần xác định đường dẫn và tên tệp đầy đủ của tập lệnh trong khi thực thi. Tôi phát hiện ra rằng tùy thuộc vào cách bạn gọi tập lệnh $0 khác nhau và đôi khi có chứa fullpath+filename và đôi khi chỉ là filename. Bởi vì thư mục làm việc có thể khác nhau, tôi không thể nghĩ ra một cách đáng tin cậy để lấy fullpath+filename của tập lệnh.

Bất cứ ai cũng có một giải pháp?

156
Chris Madden

Có một số cách:

  • $0 là tập lệnh hiện đang được cung cấp bởi POSIX, liên quan đến thư mục làm việc hiện tại nếu tập lệnh nằm ở hoặc bên dưới CWD
  • Ngoài ra, cwd(), getcwd()abs_path() được cung cấp bởi mô đun Cwd và cho bạn biết kịch bản đang được chạy từ đâu
  • Mô-đun FindBin cung cấp các biến $Bin & $RealBinthường là đường dẫn đến tập lệnh thực thi; mô-đun này cũng cung cấp $Script & $RealScript là tên của tập lệnh
  • __FILE__ là tệp thực tế mà trình thông dịch Perl xử lý trong quá trình biên dịch, bao gồm cả đường dẫn đầy đủ của nó.

Tôi đã thấy ba mô-đun đầu tiên ( $0 , CwdFindBin ) thất bại trong mod_Perl một cách ngoạn mục, tạo ra đầu ra vô giá trị như '.' hoặc một chuỗi trống. Trong các môi trường như vậy, tôi sử dụng __FILE__ và nhận đường dẫn từ đó bằng cách sử dụng mô đun File::Basename :

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

$ 0 thường là tên chương trình của bạn, vậy còn cái này thì sao?

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

Dường như với tôi rằng điều này sẽ hoạt động như abs_path biết nếu bạn đang sử dụng một đường dẫn tương đối hoặc tuyệt đối.

Cập nhật Đối với bất kỳ ai đọc những năm sau này, bạn nên đọc câu trả lời của Drew bên dưới. Nó tốt hơn tôi nhiều.

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

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

34
Mark

Tôi nghĩ rằng mô-đun bạn đang tìm kiếm là FindBin:

#!/usr/bin/Perl
use FindBin;

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

Bạn có thể sử dụng FindBin , Cwd , File :: Basename hoặc kết hợp chúng. Tất cả đều nằm trong bản phân phối cơ bản của Perl IIRC.

Tôi đã sử dụng Cwd trong quá khứ:

Cwd:

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

Lấy đường dẫn tuyệt đối đến $0 hoặc __FILE__ là những gì bạn muốn. Rắc rối duy nhất là nếu ai đó đã thực hiện chdir()$0 là tương đối - thì bạn cần có đường dẫn tuyệt đối trong BEGIN{} để ngăn chặn mọi bất ngờ.

FindBin cố gắng thực hiện tốt hơn và mò mẫm trong $PATH để tìm thứ gì đó phù hợp với basename($0), nhưng có những lúc điều đó quá đáng ngạc nhiên (cụ thể: khi tệp "ở ngay trước mặt bạn" trong cwd.)

File::FuFile::Fu->program_nameFile::Fu->program_dir cho việc này.

9
Eric Wilhelm

Một số nền tảng ngắn:

Thật không may, API Unix không cung cấp một chương trình đang chạy với đường dẫn đầy đủ đến tệp thực thi. Trong thực tế, chương trình thực thi của bạn có thể cung cấp bất cứ điều gì nó muốn trong trường thường cho chương trình của bạn biết nó là gì. Có, như tất cả các câu trả lời chỉ ra, các phương pháp phỏng đoán khác nhau để tìm kiếm các ứng cử viên có khả năng. Nhưng không có gì ngắn gọn khi tìm kiếm toàn bộ hệ thống tập tin sẽ luôn hoạt động và thậm chí điều đó sẽ thất bại nếu thực thi được di chuyển hoặc loại bỏ.

Nhưng bạn không muốn Perl thực thi, đó là những gì thực sự đang chạy, nhưng kịch bản mà nó đang thực thi. Và Perl cần biết kịch bản ở đâu để tìm thấy nó. Nó lưu trữ cái này trong __FILE__, trong khi $0 là từ API Unix. Đây vẫn có thể là một đường dẫn tương đối, vì vậy hãy lấy gợi ý của Mark và chuẩn hóa nó với File::Spec->rel2abs( __FILE__ );

7
wnoise

Để có được đường dẫn đến thư mục chứa tập lệnh của tôi, tôi đã sử dụng kết hợp các câu trả lời đã cho.

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

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

Bạn đã thử chưa:

$ENV{'SCRIPT_NAME'}

hoặc là

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

Nó thực sự phụ thuộc vào cách nó được gọi và nếu nó là CGI hoặc được chạy từ Shell bình thường, v.v.

6
Sean

perlfaq8 trả lời một câu hỏi rất giống với việc sử dụng hàm rel2abs() trên $0. Chức năng đó có thể được tìm thấy trong File :: Spec.

2
moritz

Không cần sử dụng các mô-đun bên ngoài, chỉ với một dòng bạn có thể có tên tệp và đường dẫn tương đối. Nếu bạn đang sử dụng các mô-đun và cần áp dụng một đường dẫn liên quan đến thư mục script, thì đường dẫn tương đối là đủ.

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

Bạn đang tìm kiếm điều này?:

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

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

Đầu ra sẽ như thế này:

You are running MyFileName.pl now.

Nó hoạt động trên cả Windows và 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

Không có câu trả lời "hàng đầu" nào phù hợp với tôi. Vấn đề với việc sử dụng FindBin '$ Bin' hoặc Cwd là chúng trả về đường dẫn tuyệt đối với tất cả các liên kết tượng trưng được giải quyết. Trong trường hợp của tôi, tôi cần đường dẫn chính xác với các liên kết tượng trưng - giống như trả về lệnh Unix "pwd" chứ không phải "pwd -P". Hàm sau cung cấp giải pháp:

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

Vấn đề với việc chỉ sử dụng dirname(__FILE__) là nó không tuân theo các liên kết tượng trưng. Tôi đã phải sử dụng điều này cho kịch bản của mình để theo liên kết tượng trưng đến vị trí tệp thực tế.

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

Vấn đề với __FILE__ là nó sẽ in đường dẫn lõi. Tôi đoán nó phụ thuộc vào mục tiêu của bạn là gì.

Dường như với tôi rằng Cwd chỉ cần được cập nhật cho mod_Perl. Đây là gợi ý của tôi:

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;

Vui lòng thêm bất kỳ đề nghị.

0
Jonathan

Không có bất kỳ mô-đun bên ngoài nào, hợp lệ cho Shell, hoạt động tốt ngay cả với '../':

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

kiểm tra:

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

Tất cả các giải pháp không có thư viện thực sự không hoạt động trong hơn một vài cách để viết một đường dẫn (nghĩ ../ hoặc /bla/x/../bin/./x/../ vv. Giải pháp của tôi trông giống như bên dưới. Tôi có một câu châm biếm: Tôi không có ý tưởng rõ ràng nhất tại sao tôi phải chạy thay thế hai lần. Nếu tôi không, tôi nhận được một "./" hoặc "../". có vẻ khá mạnh mẽ với tôi.

  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