این موضوع به طور مکرر طرح میشود. این پاسخ فقط عبارت به کار رفته فوق («فایلهای پیکربندی») را پوشش نمیدهد، بلکه چند حالت مختلف را شامل میشود. اگر شما به اینجا هدایت شدهاید، لطفاً قبل از ترک کردن آن، تمام پاسخ را بخوانید.
این پرسش پیچیدهایست، زیرا برای آن یک پاسخ صحیح منفرد وجود ندارد. حتی وخیمتر: پیدا کردن محل اسکریپت به طور قابل اعتماد برای 100% تمام حالتها، امکانپذیر نیست. تمام روشهای یافتن محل اسکریپت وابسته به نام اسکریپت است، به طوری که در متغیر پیشتعریف شده $0 دیده شده است. اما فراهم نمودن نام اسکریپت در $0 فقط یک قرارداد(بسیار رایج) است، نه یک التزام.
پاسخ مورد تردید این است: «در برخی شلها $0 همیشه، حتی اگر شما اسکریپت را با نام مسیر نسبی، یا بدون هرگونه نام مسیر فراخوانی نمایید»، محتوی نام مسیر مطلق است. اما این در تمام پوستهها قابل اتکا نیست، بعضی از آنها(از جمله BASH) به جای نام مسیر کاملاً واجد شرایط، فرمان واقعی تایپ شده توسط کاربر را باز میگردانند. و این فقط نوک کوه یخ شناور است!
اسکریپت شما ممکن است در حقیقت به هیچ وجه در دیسک قابل دسترسی در محل قرار نداشته باشد. این مورد را ملاحظه کنید:
ssh remotehost bash < ./myscript
پوستهِ در حال اجرا در میزبان راه دور، فرمان خود را از یک لوله دریافت میکند. اسکریپتی در جایی روی دیسک، که bash بتواند ببیند وجود ندارد.
علاوه براین، حتی اگر اسکریپت شما در دیسک محلی ذخیره و اجرا شده باشد، میتواند انتقال یابد. شخصی میتواند در فاصله بین زمانی که شما فرمان را تایپ میکنید و زمان بررسی $0 اسکریپت را به محل دیگری mv نماید. یا کسی میتواند در مدت همان روزنهِ زمانی، پیوند اسکریپت را قطع کند، به طوری که دیگر به طور واقعی ارتباطی با سیستم فایل نداشته باشد.
حتی در حالتهایی که اسکریپت در یک مکان ثابت روی دیسک محلی قرار گرفته است، رویکرد $0 باز هم اشکالات عمدهای دارد. مهمتر از همه آنست که نام اسکریپ(به طوری که در $0 دیده شده) شاید نسبت به دایرکتوری کاری نباشد، بلکه وابسته به شاخهای از برنامه جستجوی مسیر $PATH باشد(این مورد اغلب در KornShell دیده شده است). یا(و این مشکل به مراتب محتملتر است) شاید به اسکریپت پیوندهای چندگانه از مکانهای مختلف وجود داشته باشد، که یکی از آنها پیوند نمادین سادهای به یک شاخه معمول در PATH مانند /usr/local/bin باشد، که اسکریپت به واسطه آن فراخوانی میشود. اسکریپت شما شاید در /opt/foobar/bin/script باشد، اما رویکرد ساده خواندن $0 آنرا به شما نخواهد گفت -- ممکن است به جای آن این آدرس را /usr/local/bin/script بگوید.
(برای بحث جامعتر در خصوص فایل سیستم یونیکس و چگونگی تأثیر پیوندهای نمادین بر توانایی شما در مورد دانستن جایی که در یک لحظه معین قرار دارید، این طرح ۹ صفحهای را ببینید.)
با تمام آنچه گفته شد، اگر بازهم میخواهید به طرف مفروضات ساده چرخش کنید، و تمام آنچه میخواهید، نگارش کاملاً واجد شرایط $0 است، میتوانید از موردی مشابه این ترکیب دستوری استفاده کنید (BASH):
[[ $0 == /* ]] && echo "$0" || echo "${PWD}/${0#./}"
یا نگارش پوسته Bourne:
case $0 in /*) echo "$0";; *) echo "`pwd`/$0";; esac
یا نوع مستقل از پوسته(به readlink(1) با پشتیبانی از -f نیاز دارد،به هرحال وابسته به سیستم عامل است):
readlink -f "$0"
در Bash، نگارش 4.1.7(1)-release، در لینوکس، به نظر میرسد همیشه اسکریپت با توصیفگرفایل 255 باز میشود، بنابراین میتوانید دقیقاً اینطور انجام بدهید:
HOME="$(dirname "$(readlink /proc/$$/fd/255)")"
اگر بخواهیم تمام وضعیتهایی را بشماریم که در آنها ممکن است نام مسیر نسبی اسکریپت(در $0) به جای دایرکتوری کاری جاری(به طوری که در فوق اشاره گردید)،نسبت به اجزاء متشکله $PATH باشد، میتوانیم جستجو برای اسکریپت در تمام شاخههای $PATH را آزمایش کنیم.
اسکریپت پایین چگونگی انجام آن را نشان میدهد:
1 #!/bin/bash
2
3 myname=$0
4 if [[ -s "$myname" ]] && [[ -x "$myname" ]]; then
5 # از قبل نام فایل معتبری است $myname
6
7 mypath=$myname
8 else
9 case "$myname" in
10 /*) exit 1;; # جستجو نمیشود PATH نام مسیر مطلق،
11 *)
12 # مراقب تفسیر ،PATH جستجوی تمام شاخههای متغیر
13 # کاراکتر ":" ابتدا و انتهای مسیر به معنی دایرکتوری
14 # نیز همین PATH جاری باشید در مورد "::" در میان
15 # .مطلب صادق است
16
17 # p تعویض : ابتدای مسیر با . و ذخیره در
18 p=${PATH/#:/.:}
19 # تعویض : انتها با .
20 p=${p//%:/:.}
21 # تعویض :: با :.:
22 p=${p//::/:.:}
23 # جداکننده فیلد موقتی، پرسش و پاسخ شماره ۱ را ببینید
24 OFS=$IFS IFS=:
25 # تفکیک مسیر نسبت به کولن و تکرار حلقه روی آنها
26 for dir in $p; do
27 [[ -f "$dir/$myname" ]] || continue # فایل نیست
28 [[ -x "$dir/$myname" ]] || continue # قابل اجرا نیست
29 mypath=$dir/$myname
30 break # فقط اولین انطباق فایل را برمیگرداند
31 done
32 # بازیابی جداکننده فیلد اولیه
33 IFS=$OFS
34 ;;
35 esac
36 fi
37
38 if [[ ! -f "$mypath" ]]; then
39 echo >&2 "cannot find full path name: $myname"
40 exit 1
41 fi
42
43 echo >&2 "path of this script: $mypath"
توجه کنید که $mypath ضرورتاً یک نام مسیر مطلق نیست. باز هم میتواند شامل نام مسیرهای نسبی مانند ../bin/myscript باشد،زیرا $PATH میتوانست شامل آنها باشد. اگر شما میخواهید فقط دایرکتوری را از آن رشته به دست آورید، پرسش و پاسخ شماره 73 را بررسی کنید.
آیا متوجه میشوید که چطور این مشکل به طور مسخرهای پیچیده میشود؟ و این هنوز درست سادهترین حالتی است که در آن، مفروضات زیادی در مورد انتقال نیافتن اسکریپت، و در لوله نبودن آن ایجاد نمودهایم!
به طور کلی، ذخیره فایلها در همان شاخه برنامههایشان عادت نامناسبی است. طرحبندی فایل سیستم یونیکس فرض میکند که فایلهای موجود در یک محل(به عنوان مثال /bin) برنامههای اجرایی هستند، در حالیکه فایلها در محل دیگر(به عنوان مثال /etc) فایلهای داده هستند. (تصور کنید برای یک لحظه از این ارثیه سیستمهای یونیکس، با قرار دادن برنامه ها در /etc صرفنظر کنیم، میتوانیم....)
در اینجا، به جای تلاش در انجام امر غیر ممکن، برخی جایگزینهای عقل سلیم را ملاحظه خواهید نمود:
واقعاً نگهداری پیکربندی اسکریپت خودتان در محل منفرد و ثابت از قبیل /etc/foobar.conf بیش از همه مفهوم واقع میشود.
اگر لازم است که چند فایل پیکربندی تعیین کنید، آنوقت میتوانید یک دایرکتوری (مثلاً /var/lib/foobar/ یا /usr/local/lib/foobar/) داشته باشید، و مکان آن دایرکتوری را از یک محل ثابت مانند /etc/foobar.conf بخوانید.
حتی اگر نمیخواهید که زیاد hard-coded(مترجم: نوشتن دادهها یا رفتار به طور مستقم در برنامه، احتمالاً در چند محل به طوری که به آسانی نمیتواند اصلاح شود) بشود، میتوانید محل foobar.conf(یاخود دایرکتوری پیکربندی) را به عنوان پارامتر به اسکریپت بدهید.
اگر نیاز دارید اسکریپت در صورت فقدان /etc/foobar.conf، پیشفرض معینی در نظر بگیرد، میتوانید پیش فرضها را در خود اسکریپت قرار بدهید، یا اگر /etc/foobar.conf غایب است، به موردی مانند $HOME/.foobar.conf بازگشت بدهید.
BASH_SOURCE احتمالاً ایده بسیار بهتری از $0 است، نتایج بهتری ارائه میکند و بهتر تعریف میشود. شاید این مقاله باید با در نظر داشتن BASH_SOURCE نوشته میشد. --Lhunath
پرسش و پاسخ 28 (آخرین ویرایش 2011-08-22 18:38:21 توسط GreyCat)