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?
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 CWDcwd()
, getcwd()
và 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ừ đâuFindBin
cung cấp các biến $Bin
& $RealBin
mà thườ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
, Cwd
và FindBin
) 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__);
$ 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.
Use File::Spec;
File::Spec->rel2abs( __FILE__ );
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";
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";
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()
và $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::Fu
có File::Fu->program_name
và File::Fu->program_dir
cho việc này.
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__ );
Để 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__));
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.
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.
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";
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.
#!/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
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
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));
}
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__);
}
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ị.
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
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/;