كيف يمكنني الحصول على المسار الكامل لبرنامج Perl النصي الذي يتم تنفيذه؟
سؤال
لدي برنامج Perl النصي وأحتاج إلى تحديد المسار الكامل واسم الملف للبرنامج النصي أثناء التنفيذ.لقد اكتشفت ذلك اعتمادًا على كيفية استدعاء البرنامج النصي $0
يختلف ويحتوي في بعض الأحيان على fullpath+filename
وأحيانا فقط filename
.نظرًا لأن دليل العمل يمكن أن يختلف أيضًا، لا يمكنني التفكير في طريقة للحصول على ملف fullpath+filename
من البرنامج النصي.
هل حصل أحد على الحل؟
المحلول
وهناك عدد قليل من الطرق:
$0
هو البرنامج النصي الذي يتم تنفيذه حاليًا كما هو منصوص عليه بواسطة POSIX، بالنسبة إلى دليل العمل الحالي إذا كان البرنامج النصي عند CWD أو أسفله- بالإضافة إلى ذلك،
cwd()
,getcwd()
وabs_path()
يتم توفيرها من قبلCwd
الوحدة وإخبارك بالمكان الذي يتم تشغيل البرنامج النصي منه - الوحدة
FindBin
يوفر$Bin
&$RealBin
المتغيرات ذلك عادة هي المسار إلى البرنامج النصي المنفذ؛توفر هذه الوحدة أيضًا$Script
&$RealScript
هذا هو اسم البرنامج النصي __FILE__
هو الملف الفعلي الذي يتعامل معه مترجم Perl أثناء الترجمة، بما في ذلك مساره الكامل.
لقد رأيت الثلاثة الأولى ($0
, ، ال Cwd
الوحدة النمطية و FindBin
الوحدة النمطية) تفشل تحت mod_perl
بشكل مذهل، وإنتاج مخرجات لا قيمة لها مثل '.'
أو سلسلة فارغة.في مثل هذه البيئات، أستخدم __FILE__
والحصول على المسار من ذلك باستخدام File::Basename
وحدة:
use File::Basename;
my $dirname = dirname(__FILE__);
نصائح أخرى
عادةً ما يكون $0 هو اسم برنامجك، فماذا عن هذا؟
use Cwd 'abs_path';
print abs_path($0);
يبدو لي أن هذا يجب أن يعمل كما يعلم abs_path إذا كنت تستخدم مسارًا نسبيًا أو مطلقًا.
تحديث لأي شخص يقرأ هذا بعد سنوات، يجب عليك قراءة إجابة درو أدناه.انها أفضل بكثير من الألغام.
Use File::Spec;
File::Spec->rel2abs( __FILE__ );
أعتقد أن الوحدة التي تبحث عنها هي FindBin:
#!/usr/bin/perl
use FindBin;
$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";
يمكنك استخدام FindBin, CWD, الملف::الاسم الأصلي, ، أو مزيج منهما.كلهم موجودون في التوزيع الأساسي لـ Perl IIRC.
لقد استخدمت Cwd في الماضي:
قرص مضغوط:
use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
الحصول على المسار المطلق ل $0
أو __FILE__
هو ما تريد.المشكلة الوحيدة هي إذا قام شخص ما chdir()
و ال $0
كان نسبيًا - فأنت بحاجة إلى الحصول على المسار المطلق في ملف BEGIN{}
لمنع أي مفاجآت.
FindBin
يحاول أن يذهب بشكل أفضل ويتذلل في $PATH
لشيء مطابق ل basename($0)
, ، ولكن هناك أوقات يؤدي فيها ذلك إلى أشياء مفاجئة للغاية (على وجه التحديد:عندما يكون الملف "أمامك مباشرة" في cwd.)
File::Fu
لديه File::Fu->program_name
و File::Fu->program_dir
لهذا.
بعض الخلفية القصيرة:
لسوء الحظ، لا توفر Unix API برنامجًا قيد التشغيل بالمسار الكامل للملف القابل للتنفيذ.في الواقع، يمكن للبرنامج الذي ينفذ برنامجك أن يقدم ما يريده في المجال الذي يخبر برنامجك عادة بما هو عليه.هناك، كما تشير جميع الإجابات، العديد من الاستدلالات للعثور على المرشحين المحتملين.لكن لن ينجح دائمًا أقل من البحث في نظام الملفات بأكمله، وحتى ذلك سيفشل إذا تم نقل الملف القابل للتنفيذ أو إزالته.
لكنك لا تريد أن يكون برنامج Perl القابل للتنفيذ، وهو ما هو قيد التشغيل بالفعل، بل تريد البرنامج النصي الذي ينفذه.ويحتاج بيرل إلى معرفة مكان وجود البرنامج النصي للعثور عليه.ويخزن هذا في __FILE__
, ، بينما $0
هو من Unix API.لا يزال من الممكن أن يكون هذا مسارًا نسبيًا، لذا خذ اقتراح مارك وقم بتطويبه File::Spec->rel2abs( __FILE__ );
هل جربت:
$ENV{'SCRIPT_NAME'}
أو
use FindBin '$Bin';
print "The script is located in $Bin.\n";
يعتمد الأمر حقًا على كيفية استدعائه وما إذا كان CGI أو يتم تشغيله من غلاف عادي، وما إلى ذلك.
من أجل الحصول على المسار إلى الدليل الذي يحتوي على البرنامج النصي الخاص بي، استخدمت مجموعة من الإجابات المقدمة بالفعل.
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;
my $dir = dirname(File::Spec->rel2abs(__FILE__));
بيرلفاك8 يجيب على سؤال مشابه جدًا باستخدام rel2abs()
وظيفة على $0
.يمكن العثور على هذه الوظيفة في File::Spec.
ليست هناك حاجة لاستخدام وحدات خارجية، فبواسطة سطر واحد فقط يمكنك الحصول على اسم الملف والمسار النسبي.إذا كنت تستخدم وحدات وتحتاج إلى تطبيق مسار متعلق بدليل البرنامج النصي، فإن المسار النسبي يكفي.
$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
#!/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";
: د.د
هل تبحث عن هذا؟:
my $thisfile = $1 if $0 =~
/\\([^\\]*)$|\/([^\/]*)$/;
print "You are running $thisfile
now.\n";
سيبدو الإخراج كما يلي:
You are running MyFileName.pl now.
وهو يعمل على كل من ويندوز ويونيكس.
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
المشكلة مع __FILE__
هو أنه سيتم طباعة مسار الوحدة الأساسية ".pm" وليس بالضرورة مسار البرنامج النصي ".cgi" أو ".pl" الذي يتم تشغيله.أعتقد أن ذلك يعتمد على هدفك.
يبدو لي أن Cwd
يحتاج فقط إلى التحديث لـ mod_Perl.وهنا اقتراحي:
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;
الرجاء إضافة أي اقتراحات.
بدون أي وحدات خارجية، صالحة لـ Shell، وتعمل بشكل جيد حتى مع '../':
my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";
امتحان:
$ /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
المشكلة مع مجرد استخدام dirname(__FILE__)
هو أنه لا يتبع الروابط الرمزية.اضطررت إلى استخدام هذا للبرنامج النصي الخاص بي لمتابعة الرابط الرمزي إلى موقع الملف الفعلي.
use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
$script_dir = dirname(readlink(__FILE__));
}
else {
$script_dir = dirname(__FILE__);
}
جميع الحلول الخالية من المكتبات لا تعمل فعليًا لأكثر من بضع طرق لكتابة المسار (فكر ../ أو /bla/x/../bin/./x/../ إلخ.الحل الخاص بي يبدو أدناه.لدي عيب واحد:ليس لدي أدنى فكرة عن سبب اضطراري إلى تشغيل البدائل مرتين.إذا لم أفعل ذلك، سأحصل على "./" أو "../" زائفة.وبصرف النظر عن ذلك، يبدو قويا جدا بالنسبة لي.
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/;
لم تكن أي من الإجابات "الأعلى" مناسبة لي.المشكلة في استخدام FindBin '$Bin' أو Cwd هي أنها تُرجع المسار المطلق مع حل جميع الروابط الرمزية.في حالتي كنت بحاجة إلى المسار الدقيق مع وجود روابط رمزية - وهو نفس الأمر الذي يُرجع أمر Unix "pwd" وليس "pwd -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));
}
ما المشكلة في $^X
?
#!/usr/bin/env perl<br>
print "This is executed by $^X\n";
سيعطيك المسار الكامل لثنائي Perl المستخدم.
إيفرت
في *nix، من المحتمل أن يكون لديك الأمر "whereis"، الذي يبحث في $PATH الخاص بك بحثًا عن ثنائي باسم معين.إذا كان $0 لا يحتوي على اسم المسار الكامل، فمن المفترض أن يخبرك تشغيل Whereis $scriptname وحفظ النتيجة في متغير بمكان وجود البرنامج النصي.