توابع میتوانند شناسههای داده شده به آنها را پردازش نموده و برای پردازش بعدی یک کد خروج به اسکریپت برگشت بدهند.
function_name $arg1 $arg2
تابع به شناسههای داده شده به وسیله مکان آنها مراجعه میکند ( همچنانکه اگر آنها پارامترهای مکانی بودند)، یعنی، $1، $2، و الی آخر.
مثال 24-2. تابع پذیرنده پارامترها
#!/bin/bash #توابع و پارامترها DEFAULT=default #مقدار پارامتر پیشفرض. func2 () { if [ -z "$1" ] #آیا طول پارامتر شماره 1 صفر است؟ then echo "-Parameter #1 is zero length.-" #یا پارامتری عبور داده نشده است. else echo "-Parameter #1 is \"$1\".-" fi variable=${1-$DEFAULT} #جایگزینی پارامتر چه نشان میدهد؟ echo "variable = $variable" #--------------------------- #باعث تشخیص میان نبودن پارامتر و #+پارامتر تهی خواهد گردید. if [ "$2" ] then echo "-Parameter #2 is \"$2\".-" fi return 0 } echo echo "Nothing passed." func2 #بدون پارامتر فراخوانی گردیده است echo echo "Zero-length parameter passed." func2 "" #با پارامتری به طول صفر فراخوانی شده echo echo "Null parameter passed." func2 "$uninitialized_param" #با پارامتر بدون مقدار اولیه، احضار شده echo echo "One parameter passed." func2 first #با یک پارامتر فراخوانی گردیده است echo echo "Two parameters passed." func2 first second #با دو پارامتر فراخوانی شده است echo echo "\"\" \"second\" passed." func2 "" second #با پارامتر اول به طول صفر و یک رشته echo #اسکی به عنوان پارامتر دوم احضار شده. exit 0
فرمان shift روی پارامترهای داده شده به توابع عمل میکند ( مثال 36-18 را ببینید ). |
اما در مورد شناسههای خط فرمان داده شده به اسکریپت چطور؟ آیا تابع آنها را میبیند؟ بسیار خوب، اجازه بدهید آشفتگی را برطرف کنیم.
مثال 24-3. توابع و شناسههای خط فرمان عبور داده شده به اسکریپت
#!/bin/bash # #این اسکریپت را با یک شناسه خط فرمان #+فراخوانی کنید، چیزی مشابه $0 arg1 func () { echo "$1" #شناسه اول داده شده به تابع را نشان میدهد. } #آیا یک شناسه خط فرمان را شناسایی میکند؟ echo "First call to function: no arg passed." echo "See if command-line arg is seen." func #خیر! شناسه خط فرمان دیده نمیشود. echo "============================================================" echo echo "Second call to function: command-line arg passed explicitly." func $1 #اکنون دیده میشود! exit 0مترجم: به طور کلی استفاده از نماد پارامترهای مکانی در داخل تابع به معنی پارامتر مکانی تابع خواهد بود و در خارج از تابع به معنی پارامتر مکانی اسکریپت است. بنابراین در احضار تابع به صورت func $1 ابتدا $1 به پارامتر مکانی اسکریپت بسط داده میشود و سپس تابع اجرا میشود و آن پارامتر به پارامتر مکانی تابع تبدیل میشود و در تابع با نماد $1 قابل دستیابی میگردد.
در مقایسه با برخی زبانهای برنامهنویسی دیگر، اسکریپتهای پوسته به طور معمول فقط مقدار پارامترها را به توابع عبور میدهند. نام متغیرها (که در واقع اشاره گر هستند)، اگر به صورت پارامتر به تابع داده شوند، به عنوان رشتههای لفظی تلقی خواهند گردید. توابع شناسههایشان را به طور لفظی تفسیر میکنند.
ارجاعهای متغیر غیرمستقیم ( مثال 37-2 را ببینید) یک نوع مکانیسم ناهنجار برای عبور دادن اشارهگرهای متغیر به توابع فراهم میکنند.
مثال 24-4. عبور دادن یک مرجع غیرمستقیم به یک تابع
#!/bin/bash #عبور دادن یک مرجع غیرمستقیم به یک تابع. echo_var () { echo "$1" } message=Hello Hello=Goodbye echo_var "$message" #Hello #اکنون، بیایید یک مرجع غیرمستقیم به تابع بدهیم. echo_var "${!message}" #Goodbye echo "-------------" #اگر محتویات متغیر hello را تغییر بدهیم چه اتفاقی میافتد؟ Hello="Hello, again!" echo_var "$message" #Hello echo_var "${!message}" #Hello, again! exit 0
پرسش منطقی بعدی آن است که آیا بعد از اینکه پارامترها به تابع عبور داده شدند، میتوانند dereference بشوند.
مثال 24-5. dereference کردن پارامتر عبور داده شده به یک تابع
#!/bin/bash # #dereference کردن پارامتر عبور داده شده به یک تابع. #اسکریپت نوشته Bruce W. Clare dereference () { y=\$"$1" #نام متغیر (نه محتوای آن!). echo $y #$Junk x=`eval "expr \"$y\" "` echo $1=$x eval "$1=\"Some Different Text \"" #تخصیص مقدار جدید. } Junk="Some Text" echo $Junk "before" #Some Text before dereference Junk echo $Junk "after" #Some Different Text after exit 0
مثال 24-6. بار دیگر، dereference کردن پارامتر عبور داده شده به یک تابع
#!/bin/bash #dereference کردن پارامتر عبور داده شده به یک تابع. #(مثال پیچیده) ITERATIONS=3 #تعداد دریافتهای ورودی. icount=1 my_read () { #my_read فراخوانی شده با نام متغیر، مقدار قبلی را به عنوان مقدار #+پیشفرض، میان براکتها بیرون میدهد، سپس مقدار جدید را جویا میشود. local local_var echo -n "Enter a value " eval 'echo -n "[$'$1'] "' #مقدار قبلی. # #فهمیدن آن آسانتر است، اما در اعلان به #+کاربر، فاصله انتهایی را از دست میدهد. read local_var [ -n "$local_var" ] && eval $1=\$local_var #And-list: اگر local_var موجود است، آنوقت $1 مساوی مقدار آن بشود. } echo while [ "$icount" -le "$ITERATIONS" ] do my_read var echo "Entry #$icount = $var" let "icount += 1" echo done #تشکر از Stephane Chazelas برای در اختیار گذاشتن این مثال آموزنده. exit 0
توابع یک مقدار را که وضعیت خروج نامیده میشود برگشت میدهند. این مورد، قابل قیاس با وضعیت خروج برگشت شده به وسیله یک فرمان است. وضعیت خروج میتواند به طور صریح با یک دستور return مشخص بشود، در غیر اینصورت برابر با وضعیت خروج آخرین فرمان اجرا شده در تابع خواهد بود (0 اگر موفق باشد، و یک کد خطای غیرصفر اگر موفق نباشد). این وضعیت خروج با مراجعه به آن به عنوان $? میتواند در اسکریپت استفاده گردد. این ساز و کار به طور موثر اجازه میدهد که توابع اسکریپت، دارای یک «مقدار برگشتی» مشابه توابع C باشند.
یک تابع را خاتمه میدهد. یک فرمان return [1] به طور اختیاری یک شناسه عدد صحیح میپذیرد، که به عنوان «وضعیت خروج» به اسکریپت احضار کننده تابع برگشت داده میشود، این وضعیت خروج به متغیر $? تخصیص داده میشود.
#!/bin/bash #ماکزیمم دو عدد صحیح. E_PARAM_ERR=250 #اگر کمتر از دو پارامتر به تابع داده شده باشد. EQUAL=251 #مقدار برگشتی در صورتیکه دو پارامتر مساوی باشند. #مقادیر خطا خارج از محدوده اعدادی است که میتوان به تابع عبور داد. max2 () #از بین دوعدد، عدد بزرگتر را برگشت میدهد. { #توجه: اعداد مورد مقایسه باید کوچکتر از 250 باشند. if [ -z "$2" ] then return $E_PARAM_ERR fi if [ "$1" -eq "$2" ] then return $EQUAL else if [ "$1" -gt "$2" ] then return $1 else return $2 fi fi } max2 $1 $2 return_val=$? if [ "$return_val" -eq $E_PARAM_ERR ] then echo "Need to pass two parameters to the function." elif [ "$return_val" -eq $EQUAL ] then echo "The two numbers are equal." else echo "The larger of the two numbers is $return_val." fi exit 0 #تمرین (آسان): #------------------- #این اسکریپت را به یک اسکریپت محاورهای تبدیل کنید، #+یعنی، اسکریپت (دو عدد) ورودی را درخواست نماید.
برای اینکه تابع یک رشته یا آرایه را برگشت بدهد، از یک متغیر اختصاصی استفاده کنید. count_lines_in_etc_passwd() { [[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd)) # |
مثال 24-8. تبدیل عددها به اعداد رومی
#!/bin/bash #تبدیل اعداد عربی به اعداد رومی #محدوده: 0 - 200 #ناپخته است، اما کار میکند. #بسط محدوده و بهبودبخشی آن به عنوان یک تمرین واگذار شده است. #نحوه کاربرد: roman number-to-convert LIMIT=200 E_ARG_ERR=65 E_OUT_OF_RANGE=66 if [ -z "$1" ] then echo "Usage: `basename $0` number-to-convert" exit $E_ARG_ERR fi num=$1 if [ "$num" -gt $LIMIT ] then echo "Out of range!" exit $E_OUT_OF_RANGE fi to_roman () #تابع باید قبل از اولین فراخوانی آن، تعریف بشود. { number=$1 factor=$2 rchar=$3 let "remainder = number - factor" while [ "$remainder" -ge 0 ] do echo -n $rchar let "number -= factor" let "remainder = number - factor" done return $number #تمرینها: #------------------------ #(1 چگونگی کارکرد تابع را تشریح کنید. #اشاره: جداسازی به وسیله تفریق پی در پی. #(2 محدوده تابع را گسترش بدهید. #اشاره: echo و جایگزینی فرمان را به کار ببرید. } to_roman $num 100 C num=$? to_roman $num 90 LXXXX num=$? to_roman $num 50 L num=$? to_roman $num 40 XL num=$? to_roman $num 10 X num=$? to_roman $num 9 IX num=$? to_roman $num 5 V num=$? to_roman $num 4 IV num=$? to_roman $num 1 I #فراخوانیهای متوالی تابع تبدیل! #براستی این لازم است؟؟؟ میتواند ساده شود؟ echo exit
مثال 11-29 را هم ببینید.
بزرگترین عدد صحیح مثبتی که یک تابع میتواند برگشت بدهد 255 است. فرمان return دقیقاً وابسته به مفهوم وضعیت خروج است، که موجب این محدودیت خاص است. خوشبختانه، برای آن موقعیتهایی که نیازمند برگشت دادن عدد صحیح بزرگ از یک تابع هستند، راهحلهای موقتی متنوعی وجود دارد. مثال 24-9. آزمایش مقادیر برگشتی بزرگ در یک تابع #!/bin/bash # # یک راه حل موقت برای به دست آوردن «مقادیر برگشتی» صحیح بزرگ، فقط تخصیص دادن «مقدار برگشتی» به یک متغیر سراسری است. Return_Val= # یک شیوه بیشتر برازنده، داشتن تابعی که «مقدار برگشتی» را به خروجی استاندارد، echo میکند و سپس ضبط نمودن آن به وسیله جایگزینی فرمان است. گفتار در این مورد از بخش 36.7 را ملاحظه نمایید. مثال 24-10. مقایسه دو عدد صحیح بزرگ #!/bin/bash # این هم یک مثال دیگر از ضبط کردن «مقدار برگشتی» یک تابع. فهمیدن آن نیازمند مقداری آگاهی از awk است. month_length () # همچنین مثال A-7 و مثال A-37 را ببینید. تمرین: با استفاده از آنچه اکنون آموختهایم، مثال اعداد رومی قبل را برای پذیرفتن دلخواهانه عدد ورودی بزرگ توسعه بدهید. |
در اصل تابع یک بلوک کد است که میتواند از طریق ورودی استانداردش تغییر مسیر داده شود (همچون در مثال 3-1).
مثال 24-11. نام واقعی از روی نام کاربری
#!/bin/bash # # #بواسطه نام کاربری، نام واقعی را از /etc/passwd به دست میآورد. ARGCOUNT=1 #تعداد شناسههای مورد نیاز. E_WRONGARGS=85 file=/etc/passwd pattern=$1 if [ $# -ne "$ARGCOUNT" ] then echo "Usage: `basename $0` USERNAME" exit $E_WRONGARGS fi file_excerpt () #پویش فایل برای الگو، سپس چاپ بخش مناسب سطر. { while read line #while لزوماً به [ condition ] نیاز ندارد do echo "$line" | grep $1 | awk -F":" '{ print $5 }' #awk وادار به استفاده از جداکننده «:» میشود. done } <$file #تغییر مسیر به stdin تابع. file_excerpt $pattern #بله، تمام این اسکریپت میتواند کاهش داده شود به # #یا # #یا # #نام واقعی از روی نام کاربری #به هر حال، شاید این به آن اندازه آموزنده نباشد. exit 0
یک شیوه دیگر و شاید کمتر گیج کننده برای تغییر مسیر دادن stdin تابع وجود دارد. این شیوه، مستلزم تغییر مسیر دادن stdin به یک بلوک کد است که در براکتها قرار گرفته و در داخل تابع جاسازی شده است.
Function () #به جای: { ... } < file #این را امتحان کنید: Function () { { ... } < file } #به طور مشابهی Function () #این هم کار میکند. { { echo $* } | tr a b } Function () #این کار نمیکند. { echo $* } | tr a b #اینجا بلوک کد تودرتو الزامی است. #با تشکر از S.C.
فایل bashrc نمونه Emmanuel Rouat شامل برخی مثالهای آموزنده از توابع است. |
[1] | فرمان return یک فرمان داخلی Bash است. |