فصل 4. معرفی متغیرها و پارامترها

‎4.4‎- انواع متغیر خاص

متغیرهای محلی

متغیرهای قابل مشاهده فقط در داخل قطعه کد یا تابع (همچنین متغیرهای محلی در توابع را ببینید)

متغیرهای محیطی

متغیرهایی که بر رفتار پوسته و رابط کاربر اثر می‌گذارند


در یک مفهوم کلی‌تر، هر پردازش دارای یک «محیط» است، یعنی یک گروه از متغیرها که پردازش می‌تواند به آنها رجوع کند. از این جهت، پوسته نیز مانند هر پردازش دیگری رفتار می‌نماید.

هر نوبت که یک پوسته شروع می‌شود، متغیرهای پوسته‌ای تولید می‌کند که مطابق متغیرهای محیط خودش می‌باشد. به روزرسانی یا افزودن متغیرهای محیطی جدید باعث می‌شود پوسته محیطش را به روزرسانی کند، و تمام پردازشهای فرزند پوسته (فرمانهایی که اجرا می‌کند) این محیط را به ارث ببرند.


Caution

فضای اختصاص‌یافته به محیط محدود است. ایجاد متغیرهای محیطی بسیار زیاد یا یک متغیری که فضای مفرطی مصرف می‌کند، ممکن است باعث مشکلاتی گردد.

bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"

bash$ du
bash: /usr/bin/du: Argument list too long

یادداشت: این خطا از کرنل نگارش ‎2.6.23 برطرف گردیده است‎.

(تشکر از Stéphane Chazelas برای شفاف‌سازی و ارائه مثال فوق.)


اگر یک اسکریپت متغیرهای محیطی تنظیم کند، لازم است آن متغیرها «صادر» بشوند یعنی، به محیط داخلی اسکریپت گزارش بشوند. این کار، وظیفه فرمان export است.

یک اسکریپت فقط می‌تواند متغیرها را به پردازش‌های فرزند export نماید، یعنی تنها به فرمانها یا پردازشهایی که آن اسکریپت بخصوص آنها را آغاز کرده است. یک اسکریپتِ فراخوانی شده از خط فرمان نمی‌تواند متغیرها را به عقب، به خط فرمان صادر نماید. پردازشهای فرزند نمی‌توانند متغیرها را به پردازشهای والدی که آنها را تولید مثل کرده‌اند صادر کنند.

تعریف: یک پردازش فرزند، پردازش فرعی راه‌اندازی شده توسط پردازش دیگر، یعنی پدرش است.


پارامترهای مکانی

شناسه‌های عبور داده شده از خط فرمان به اسکریپت [1]: ‎$0‎‏، ‎$1‎‏، ‎$2‎‏، ‎$3‎ ‏‏. . .

‎$0‎ نام خود اسکریپت است، ‎$1‎ اولین شناسه است، ‎$2‎ دومین، ‎$3‎ سومین، و به همین ترتیب. [2] بعد از ‎$9‎، شناسه‌ها باید در ابروها محصور بشوند، برای مثال، ‎${10}‎‏، ‎${11}‎‏، ‎${12}‎.

متغیرهای خاص ‎$*‎ و ‎$@‎ تمامِ پارامترهای مکانی را مشخص می‌کنند.

مثال ‎4-5‎. پارامترهای مکانی

#!/bin/bash

# :این اسکریپت را حداقل با ۱۰ پارامتر فراخوانی کنید، به عنوان مثال‎
# ./scriptname 1 2 3 4 5 6 7 8 9 10

MINPARAMS=10

echo

echo "The name of this script is \"$0\"."
#                را اضافه کنید ‎./‎ برای دایرکتوری جاری‎
echo "The name of this script is \"`basename $0`\"."
# (را ملاحظه کنید ‎basename‎)بیرون کشیدن اطلاعات نام مسیر ‎

echo

if [ -n "$1" ]              #          .متغیر بررسی شونده نقل‌قول می‌شود‎
then
 echo "Parameter #1 is $1"  # نقل‌قول‌ها لازم هستند ‎#‎ برای معافیت کاراکترِ‎
fi 

if [ -n "$2" ]
then
 echo "Parameter #2 is $2"
fi 

if [ -n "$3" ]
then
 echo "Parameter #3 is $3"
fi 

# ...


if [ -n "${10}" ]  # محصور بشوند ‎{ }‎ باید در ‎$9‎ پارامترهای بزرگتر از‎
then
 echo "Parameter #10 is ${10}"
fi 

echo "-----------------------------------"
echo "All the command-line parameters are: "$*""

if [ $# -lt "$MINPARAMS" ]
then
  echo
  echo "This script needs at least $MINPARAMS command-line arguments!"
fi  

echo

exit 0

نشانه‌گذاری Bracket (ابرو) برای پارامترهای مکانی، به روش نسبتاً ساده‌ای برای ارجاع به آخرین شناسه‌ای که در خط فرمان به اسکریپت عبور داده شده، منجر می‌گردد. این کار به ارجاع غیر مستقیم نیز نیاز دارد.

args=$#           #     .تعداد شناسه‌های داده شده‎
lastarg=${!args}
#  است  ‎$args‎ توجه: این یک «ارجاع غیرمستقیم» به ‎


# (‎Chris Monson‎ با تشکر از)   ‎lastarg=${!#}‎  :یا‎
#     .است ‎$#‎ این یک «ارجاع غیر مستقیم» به متغیر‎
# .کار نمی‌کند ‎lastarg=${!$#}‎ توجه داشته باشید که‎

برخی اسکریپت‌ها بسته به نامی که با آن فراخوانی می‌گردند، می‌توانندعملیات متفاوتی انجام بدهند. برای انجام این کار، لازم است اسکریپت پارامتر ‎$0‎، یعنی نامی را که بواسطه آن فراخوانی شده است، بررسی کند. [3] همچنین باید پیوندهای نمادین برای تمام نام‌های پیشنهادی اسکریپت وجود داشته باشد. مثال ‎16-2‎.

Tip

اگر اسکریپتی یک پارامتر خط فرمان انتظار داشته باشد اما بدون یک پارامتر فراخوانی گردد، این کار ممکن است موجب یک تخصیص متغیر تهی، یا به طور کلی یک نتیجه نامطلوب بشود. یک راه پیشگیری از آن، درج کردن یک کاراکتر اضافی در هر دو طرف جمله تخصیص به کار رفته برای پارامتر مکانی مورد نظر است.


variable1_=$1_  #  ‎variable1=$1‎ به جای‎
#            .حتی اگر پارامتر مکانی غایب باشد، این مورد از یک خطا پیشگیری می‌کند‎

critical_argument01=$variable1_

#                              .کاراکتر اضافی بعداً می‌تواند زدوده شود، مانند این‎
variable1=${variable1_/_/}
#‏(خط زیر) شروع بشود ‎underscore‎ با یک ‎$variable1_‎ آثار جانبی فقط در صورتی است که‎
#.در این روش یکی از قالب‌های جایگزینی پارامتر استفاده می‌شود که بعداً بحث خواهد شد‎

# یک روش قابل فهم‌ترِ برخورد با این مورد آن است که به سادگی‎
#+ .بررسی شود آیا پارامتر مکانی مورد انتظار عبور داده شده‎
if [ -z $1 ]
then
  exit $E_MISSING_POS_PARAM
fi


#  اشاره می‌کند، روش‎Fabian Kreutz‎ به هر حال، همانطور که‎
#+  .فوق می‌تواند آثار جانبی غیر قابل انتظار داشته باشد‎
#                :یک روش مناسب‌تر، جایگزینی پارامتر است‎
#  ${1:-$DefaultVal}
#                    .فصل «جایگزینی پارامتر» را ببینید‎

---

مثال ‎4-6‎. wh، مراجعه whois به نام قلمرو

#!/bin/bash
# ex18.sh

# :روی هر یک از ۳ سرویس‌دهنده پیشنهادی انجام می‌دهد ‎'whois domain-name'‎ یک مراجعه‎
#                    ripe.net, cw.net, radb.net

# .قرار بدهید ‎/usr/local/bin‎  ‏) را در‎'wh'‎ اسکریپت(تغییر نام یافته به ‎

#                 :نیازمند وجود پیوندهای زیر است‎
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
# ln -s /usr/local/bin/wh /usr/local/bin/wh-apnic
# ln -s /usr/local/bin/wh /usr/local/bin/wh-tucows

E_NOARGS=75


if [ -z "$1" ]
then
  echo "Usage: `basename $0` [domain-name]"
  exit $E_NOARGS
fi

#   .بررسی نام اسکریپت و فراخوانی سرویس‌دهنده مناسب‎
case `basename $0` in    #   case ${0##*/} in  :یا 
    "wh"       ) whois $1@whois.tucows.com;;
    "wh-ripe"  ) whois $1@whois.ripe.net;;
    "wh-apnic" ) whois $1@whois.apnic.net;;
    "wh-cw"    ) whois $1@whois.cw.net;;
    *          ) echo "Usage: `basename $0` [domain-name]";;
esac 

exit $?

---

فرمان shiftدر حقیقت پارامترهای مکانی را در اثر انتقال آنها یک پارامتر به چپ، دوباره اختصاص می‌دهد.

‎$1‎ ‎<--- $2‎‏، ‎$2 <--- $3‎‏، ‎$3 <--- $4‎‏، وغیره.

پارامتر ‎$1‎ قدیمی ناپدید می‌گردد، اما ‎$0‎ (نام اسکریپت) تغییر نمی‌کند. اگر شما تعداد پارامترهای مکانی بسیاری برای یک اسکریپت به کار ببرید، فرمان shift به شما اجازه می‌دهد به پارامترهای از ‎10‎ به بعد دسترسی داشته باشید، اگرچه نشانه‌گذاری ‎{bracket}‎ نیز این کار را میسر می‌سازد.

مثال ‎4-7‎. کاربرد shift

#!/bin/bash
#  shft.sh:      .برای گرفتن مرحله به مرحله تمام پارامترهای مکانی ‎

#  .نام گذاری کنید ‎shft.sh‎ این اسکریپت را چیزی مانند‎
#+      :و با چند پارامتر فراخوانی‌اش کنید، برای مثال‎
#             sh shft.sh a b c def 83 barndoor

until [ -z "$1" ]  # تا موقعی که تمام پارامترها به کار رفته باشند‎
do
  echo -n "$1 "
  shift
done

echo               # .تعویض سطر اضافی‎

# اما برای پارامترهای «استفاده شده» چه اتفاقی می‌افتد؟‎
echo "$2"
#  !چیزی نمایش داده نمی‌شود

#   وجود ندارد)‏ ‎$2‎ نیز برای انتقال به ‎$3‎ انتقال می‌یابد (و ‎$1 به ‎$2‎ وقتی‎‎
#+                                                .خالی می‌ماند ‎$2‎ آنوقت‎
#                    .است ‎*move*‎ پارامتر نیست، بلکه یک ‎*copy*‎ پس این یک‎

exit

#  را برای یک روش ‎echo-params.sh‎ همچنین اسکریپت ‎
#+    .جایگزین جهت مرور پارامترهای مکانی ببینید‎

فرمان shift می‌تواند یک پارامتر عددی تعیین کننده تعداد جابجایی را بپذیرد.

#!/bin/bash
# shift-past.sh

shift 3    # .سه مرتبه جابجایی‎
#  n=3; shift $n
# .دارای همان نتیجه است

echo "$1"

exit 0

# ======================== #


$ sh shift-past.sh 1 2 3 4 5
4

#     اشاره می‌کند ‎Eleni Fragkiadaki‎ به هر حال، همچنانکه ‎
#+   ‎($#)‎تلاش برای جابجایی بیش از تعداد پارامترهای مکانی‎
#+  برگشت می‌دهند و پارامترهای مکانی خودشان ‎1‎ یک کد خروج ‎
#+                                       .تغییر نمی‌کنند‎
#  این به معنی احتمال گرفتار شدن در یک حلقه بی‌پایان است‎
#  :برای مثال‎
#      until [ -z "$1" ]
#      do
#         echo -n "$1 "
#         shift 20    #  باشد،‏ ‎20‎ اگر پارامترهای مکانی کمتر از‎
#      done           #+        !آنوقت حلقه هرگز خاتمه نمی‌یابد‎
#
# موقعی که شک دارید، یک بررسی صحت انجام بدهید‎
#           shift 20 || break
#                    ^^^^^^^^


فرمان shift با پارامترهای عبور داده شده به یک تابع نیز به سبک مشابهی کار می‌کند. مثال ‎36-18‎ را ببینید.

یادداشت‌ها

[1]

توجه نمایید که تابع‌ها نیز پارامترهای مکانی می‌گیرند.

[2]

پردازش فراخواننده اسکریپت پارامتر ‎$0‎ را تنظیم می‌کند. مطابق قرارداد، این پارامتر نام اسکریپت است. manpage (صفحه مستندات) برای execv را ببینید.

با وجود این از خط فرمان، ‎$0‎ نام پوسته است.

bash$ echo $0
bash

tcsh% echo $0
tcsh

[3]

اگر اسکریپت منبع شده یا لینک شده باشد، آنوقت این مورد عمل نخواهد کرد. مطمئن‌تر است که ‎$BASH_Source‎ بررسی گردد.