مانند زبانهای برنامهنویسی «واقعی»، Bash هم دارای توابع است، اگرچه در یک پیادهسازی نسبتا محدود. تابع یک subroutine (روال فرعی) است، یک بلوک کد که مجموعهای از عملیات را انجام میدهد، یک «جعبه سیاه» که وظیفه تعیین شدهای انجام میدهد. آنجا که کد تکراری وجود دارد، موقعی که یک وظیفه با اندک تغییری در طرز کار تکرار میشود، در آن هنگام استفاده از یک تابع را در نظر بگیرید.
functionfunction_name {
command...
}
function_name () {
command...
}
این شکل دوم، برای برنامهنویسان C خوشایند خواهد بود (و بیشتر قابل حمل است).
همچون در C، کمانکِ باز کردن تابع میتواند در سطر دوم قرار بگیرد.
function_name ()
{
command...
}
یک تابع میتواند در یک سطر واحد «فشرده» بشود.
fun () { echo "This is a function"; echo; } # اما، در این حالت، باید یک semicolon بعد از فرمان انتهایی تابع قرار بگیرد. fun () { echo "This is a function"; echo } # # fun2 () { echo "Even a single-command function? Yes!"; } # |
توابع triggered (ماشه کشیده) نامیده میشوند، به سادگی به وسیله نامشان فراخوانی میگردند. یک فراخوان تابع معادل یک فرمان است.
#!/bin/bash #تمرین توابع (ساده). JUST_A_SECOND=1 funky () { #این تقریباً سادهترین حالت توابع است. echo "This is a funky function." echo "Now exiting funky function." } #تعریف تابع باید قبل از فراخوانی آن باشد. fun () { #یک تابع تا اندازهای پیچیدهتر. i=0 REPEATS=30 echo echo "And now the fun really begins." echo sleep $JUST_A_SECOND #هی، یک ثانیه منتظر بمان! while [ $i -lt $REPEATS ] do echo "----------FUNCTIONS---------->" echo "<------------ARE-------------" echo "<------------FUN------------>" echo let "i+=1" done } #اکنون، فراخوانی تابعها. funky fun exit $?
تعریف تابع باید مقدم بر اولین فراخوانی آن باشد. روشی برای «اعلان کردن» تابع، به طور مثال، همچون در C وجود ندارد.
f1 #یک پیغام خطا ارایه خواهد نمود، چون هنوز تابع f1 تعریف نشده. declare -f f1 #این هم کمکی نمیکند. f1 #بازهم یک پیغام خطا. #اما... f1 () { echo "Calling function \"f2\" from within function \"f1\"." f2 } f2 () { echo "Function \"f2\"." } f1 #تابع f2 در حقیقت تا این نقطه فراخوانی نشده، اگرچه قبل از #+تعریف شدن آن، مورد ارجاع قرار گرفته است. این مجاز است. #با تشکر از S.C
تابع حتی میتواند داخل یک تابع دیگر جای بگیرد، هرچند که این کار خیلی سودمند نیست.
f1 () { f2 () #تو در تو { echo "Function \"f2\", inside \"f1\"." } } f2 #یک پیغام خطا میدهد. حتی اضافه کردن یک #«declare -f f2» قبل از آن، کمکی نمیکند. echo f1 #کاری انجام نمیدهد، چون فراخوانی f1 به طور خودکار f2 را احضار نمیکند. f2 #اکنون، فراخوانی کردن f2 درست است، چون به وسیله فراخوانی f1 تعریف آن #+قابل رویت گردیده است. #با تشکر از S.C.
تعریف توابع میتواند در مکانهای غیرمحتمل ظاهر گردد، حتی در جایی که بدون آن تعریف، یک فرمان عمل خواهد کرد.
ls -l | foo() { echo "foo"; } #مجاز، اما بیفایده. if [ "$USER" = bozo ] then bozo_greet () #تعریف تابع در یک ساختار if/then تعبیه گردیده است. { echo "Hello, Bozo." } fi bozo_greet #فقط برای Bozo کار میکند، سایر کاربران یک خطا دریافت میکنند. #موردی مانند این، در برخی مضمونها میتواند مفید باشد. NO_EXIT=1 #تعریف تابع زیر را فعال خواهد نمود. [[ $NO_EXIT -eq 1 ]] && exit() { true; } #تعریف تابع در یک «and-list» #اگر $NO_EXIT برابر 1 باشد، تابع exit () را تعریف میکند. #تابع، با جانشین کردن true فرمان داخلی exit را غیرفعال میکند. exit #تابع exit () را فراخوانی میکند، نه فرمان داخلی exit را. #یا، به طور مشابه: filename=file1 [ -f "$filename" ] && foo () { rm -f "$filename"; echo "File "$filename" deleted."; } || foo () { echo "File "$filename" not found."; touch bar; } foo #با تشکر از S.C. و Christopher Head
توابع میتوانند دارای نامهایی با شکلهای مرموز باشند.
_(){ for i in {1..10}; do echo -n "$FUNCNAME"; done; echo; } #همچنین مثال A-56 را ببینیدبدون فاصله میان نام تابع و پرانتزها. #این همیشه کار نمیکند. چرا؟ #اکنون بیایید تابع را فراخوانی نماییم. _ # #10 کاراکتر خط زیر (10 بار نام تابع)! #یک خط زیر تنها، یک نام تابع قابل قبول است. #در عمل، یک کولن نیز یک نام تابع قابل قبول است. :(){ echo ":"; }; : #مورد استفاده این چیست؟ #روشی منحرف برای مبهم نمودن کد در یک اسکریپت است.
وقتی نگارشهای مختلفی از یک تابع در اسکریپت ظاهر شوند، چه اتفاق میافتد؟
# |