فصل دهم - کنترل جریان
در این فصل و فصل بعد دستورات کنترل کنندهی جریان، if و case مورد بررسی قرار میگیرند.
محتویات[نهفتن] |
ساختار if
دستور if اجازهی انجام دادن عملی را بسته به اینکه شرط مورد نظر درست باشد یا نباشد، میدهد.
نکته: در ساختار if نکتهای که در ابتدا ممکن است کمی ابهام آمیز باشد، شرط درستی شرط آن است. یک عبارت شرطی در صورت بازگرداندن صفر درست و در صورت بازگرداندن مقداری غیر از صفر نادرست است.
ساختار کلی دستور if به صورت زیر است:
if list1 then list2 elif list3 then list4 else list5 fi
همچنین میتوان دستور بالا را به شکل زیر نیز نوشت:
if list1 ; then list2 ; elif list3 ; then list4 ; else list5 ; fi ;
در اینجا elif و else هر دو از آپشنهای دستور if هستند. در صورتی که شما
از آپشن elif استفاده کنید نیازی به استفاده از else نیست و برعکس. همچنین
در دستور if میتوان از هر تعداد آپشن elif استفاده کرد.
در ساختار بالا شکل کلی روند اجرای دستور به شکل زیر است:
- عبارت list1 محاسبه میشود
- اگر با کد صفر از عبارت خارج شود، به معنی صحیح بودن عبارت است و در نتیجه دستور list2 انجام میشود و بعد از آن از کل ساختار if خارج میشود
- در غیر اینصورت، عبارت list3 محاسبه میشود و کد خروج آن بررسی میشود
- اگر بعد از محاسبه عبارت list3 کد صفر برگشت داده شود، دستور list4 اجرا شده و از ساختار if خارج میشویم
- اگر list3 صفر را برنگرداند، دستور list5 اجرا میشود
مثال:
if uuencode koala.gif koala.gif > koala.uu ; then echo "Encoded koala.gif to koala.uu" else echo "Error encoding koala.gif" fi
در این ساختار در صورتی که دستور uuencode koala.gif koala.gif > koala.uu با موفقیت انجام شود عبارت Encoded koala.gif to koala.uu بر روی صفحه نمایش چاپ میشود در غیر اینصورت خروجی Error encoding koala.gif را نمایش میدهد.
اشاره: در این ساختار هر دو دستور if و then در یک خط نوشته شدهاند. بیشتر برنامهنویسهای شل ترجیح میدهند ساختار if را به اختصار به این صورت بنویسند. و ادعا دارند که این فرم برای ساختار if مناسبتر است.
اشتباهات رایج:
- حذف سمیکالن ; قبل از then در شکل خطی (مثال آخر)
- استفاده از else if یا eslif به جای elif
- حذف then زمانی که در ساختار از elif استفاده میشود
- نوشتن if به جای fi در انتهای ساختار if
مثال:
در صورتی که مثال بالا را به صورت زیر و بدون سمی کالن ; قبل از then بنویسید، با خطا مواجه میشوید:
if uuencode koala.gif koala.gif > koala.uu then echo "Encoded koala.gif to koala.uu" else echo "Error encoding koala.gif" fi ch11-ex1.sh[2]: Syntax error at line 5 : `else` is not expected.
مثال: استفاده از else if به elif:
if uuencode koala.gif koala.gif > koala.uu ; then echo "Encoded koala.gif to koala.uu" else if rm koala.uu ; then echo "Encoding failed, temporary files removed." else echo "An error occured." fi
که منجر به بروز خطایی به شکل زیر میشود:
./ch11-ex1.sh: ./ch11-ex1.sh: line 8: syntax error: unexpected end of file
مثال:
در صورتی که در ساختار if عبارت then نوشته نشود، با خطا مواجه میشوید:
if uuencode koala.gif koala.gif > koala.uu ; then echo "Encoded koala.gif to koala.uu" elif rm koala.uu echo "Encoding failed, temporary files removed." else echo "An error occured." fi ./ch11-1.sh: syntax error at line 6: `else` unexpected
و در آخر اگر بجای fi از if استفاده شود، با خطای زیر مواجه میشوید:
./ch11-1.sh: syntax error at line 8: `end of file` unexpected
استفاده از test
معمولا در هر ساختار if از یک یا چند دستور test استفاده میشود.
شکل کلی دستور test به صورت زیر است:
test expression
در اینجا exprission با یکی از آپشنهای ویژه دستور test ساخته میشود. دستور test پس از ارزیابی exprission یک مقدار صفر(درست) یا غیر صفر(غلط) را برمیگرداند. شکل کوتاه شدهی دستور test به صورت زیر است:
[ expression ]
اغلب از این شکل از دستور test استفاده میشود. و exprission میتواند هر عبارت معتبری که دستور test میپذیرد باشد.
عباراتی که دستور test میتواند آنها را ارزیابی کند سه نوع زیر هستند:
- تست فایل
- مقایسه رشتهها
- مقایسه عددی
که هر سه نوع ذکر شده در این بخش بررسی میشوند. همچنین ترکیب دویا چند عبارت از سه نوع ذکر شده نیز مجاز میباشد.
تست فایل
شکل کلی این ساختار به صورت زیر است:
test option file or [ option file ]
که در اینجا file اسم فایل مورد نظر و option یکی از موارد جدول زیر است.
- -b : فایل از نوع block special باشد
- -c : فایل از نوع character special باشد
- -d : فایل موجود و یک دایرکتوری باشد
- -e : فایل موجود باشد
- -f : فایل موجود و یک فایل معمولی باشد
- -g : بیت SGID فایل برابر یک باشد
- -h : فایل از نوع لینک سمبلیک(symbolic link) باشد.
- -k : بیت sticky فایل برابر یک باشد
- -p : فایل از نوع named pipe باشد
- -r : فایل موجود و خواندنی باشد
- -s : فایل موجود و اندازهی آن بزرگتر از صفر بایت باشد
- -u : بیت suid فایل برابر یک باشد
- -w : فایل موجود و نوشتنی باشد
- -x : فایل موجود و یک فایل اجرایی باشد
- -O : فایل موجود و مالک آن کاربر باشد
مثال:
$ if [ -d /home/ranga/bin ] ; then PATH="$PATH:/home/ranga/bin" ; fi
در اینجا اگر فایل home/ranga/bin/ موجود و یک دایرکتوری باشد، دستور PATH="$PATH:/home/ranga/bin" اجرا میشود.
مثال:
$ if [ -f $HOME/.bash_aliai ] ; then . $HOME/.bash_aliai ; fi
اگر فایل HOME/.bash_aliai$ موجود باشد (فایل معمولی)، دستور HOME/.bash_aliai$ . اجرا میشود.
مثال:
if [ -s $HOME/.bash_aliai ] ; then . $HOME/.bash_aliai ; fi
در صورتی که فایل HOME/.bash_aliai$ موجود و اندازهی آن بیشتر از صفر بایت باشد، دستور HOME/.bash_aliai$ . اجرا میشود.
مقایسه رشتهها
در این ساختار تنها دو مقایسه ساده میتواند انجام گیرد:
- بررسی خالی بودن یک رشته
- بررسی برابری دو رشته
که هر کدام میتوانند صحیح یا ناصحیح باشند. جدول زیر نشان دهندهی چهار حالت تولید شده است:
- -z: در صورتی صحیح است که طول رشته برابر صفر باشد
- -n: در صورتی صحیح است که طول رشته برابر صفر نباشد
- string1 = string2 : در صورتی صحیح است که دو رشته معادل باشند
- string1 != string2 : در صورتی صحیح است که دو رشته معادل نباشند
خالی بودن یک رشته
مثال:
if [ -z "$FRUIT_BASKET" ] ; then echo "Your fruit basket is empty" ; else echo "Your fruit basket has the following fruit: $FRUIT_BASKET" fi
در صورتی که طول رشتهی FRUIT_BASKET صفر باشد عبارت Your fruit basket is empty بر روی صفحه نمایش داده میشود.
توجه: در صورتی که در مثال بالا، متغیر FRUIT_BASKET داخل "" قرار نگیرد منجر به بروز خطای زیر میشود:
test: argument expected
نمایش این خطا به این دلیل است که شل این عبارت را معادل عبارت زیر در نظر میگیرد و آرگومانی برای آن ارسال نمیکند:
[ -z ]
و در صورتی که از کاراکتر "" برای متغیر استفاده شود و طول متغیر برابر صفر باشد، عبارت معادل زیر میشود:
[ -z "" ]
مقایسه دو رشته
نکتهای در مقایسه دو رشته باید به آن توجه کرد، بزرگی و کوچکی حروف است. به عنوان مثال دو رشتهی زیر با هم معادلند:
"There are more things in heaven and earth" "There are more things in heaven and earth"
در صورتی که دو رشتهي زیر با هم برابر نیستند:
"than are dreamt of in your philosophy" "Than are dreamt of in your Philosophy"
دو مثال برای مقایسه دو رشته:
if [ "$FRUIT" = apple ] ; then echo "An apple a day keeps the doctor away." else echo "You must like doctors, your fruit $FRUIT is not an apple." fi if [ "$FRUIT" != apple ] ; then echo "You must like doctors, your fruit $FRUIT is not an apple." else echo "An apple a day keeps the doctor away." fi
مقایسه عددی
دو شکل کلی این ساختار به صورت زیر هستند:
test int1 operator int2 or [ int1 operator int2 ]
که در این ساختار int1 و int2 میتوانند دو عدد مثبت یا منفی باشند. در
صورتی هر یک از دو متغیر int1 یا int2 مقداری غیر صحیح مانند یک رشته داشته
باشد، مقدار آن صفر در نظر گرفته میشود.
و option میتواند یکی از موارد زیر باشد:
int1 -eq int2 : ( int1 = int2 ) int1 -ne int2 : ( int1 != int2 ) int1 -lt int2 : ( int1 < int2 ) int1 -le int2 : ( int1 <= int2 ) int1 -gt int2 : ( int1 > int2 ) int1 -ge int2 : ( int1 >= int2 )
مثال زیر را در نظر بگیرید:
ln -s /home/ranga/bin/bash /usr/contrib/bin if [ $? -eq 0 ] ; then echo "Command was successful." ; else echo "An error was encountered." exit fi
درصورتی که دستور خط اول بدون خطا اجرا شود شرط داخل if برقرار و عبارت Command was successful بر روی صفحه نمایش چاپ میشود.
عبارات مرکب
تا بحال عباراتی را دیدید که شامل تنها یک عبارت شرطی هستند. اما در
بسیاری موارد نیاز به ترکیب چندین عبارت شرطی داریم. به ترکیب دو یا چند
عبارات شرطی یک عبارت مرکب گفته میشود.
عبارات مرکب میتوانند با استفاده از ساختار test یا با استفاده از عملگرهای
&& و || ایجاد شوند. که معادل این عملگرها در ساختار test به صورت
زیر است:
- ! expr : در صورتی صحیح است که عبارت صحیح نباشد
- expr1 -a expr2 : expr1 && expr2 (در صورتی صحیح است که هر دو عبارت صحیح باشند)
- expr1 -o expr2 : expr1 || expr2 (در صورتی صحیح است که حداقل یکی از دو عبارت صحیح باشد)
مثال دو عبارت مرکب زیر را در نظر بگیرید (دو عبارت باهم معادل هستند):
if [ -z "$DTHOME" ] && [ -d /usr/dt ] ; then DTHOME=/usr/dt ; fi if [ -z "$DTHOME" -a -d /usr/dt ] ; then DTHOME=/usr/dt ; fi
بررسی شرط برقراری عبارت: شکل اول:
- در ابتدا [ -z "$DTHOME" ] بررسی میشود
- در صورت صحیح بودن ، [ d /usr/dt- ] بررسی میشود
- در صورت صحیح بودن دستور then اجرا میشود
شکل دوم:
- در ابتدا "z "$DTHOME- بررسی میشود
- در صورت صحیح بودن d /usr/dt- بررسی میشود
- در صورت صحیح بودن دستور then اجرا میشود
بعضی برنامه نویسها فرم اول را ترجیح میدهند، به این دلیل که هر عبارت
test به تنهایی بررسی میشود و از خوانایی بالاتری برخوردار است. و برخی
دیگر فرم دوم را میپسندند. به این دلیل که اگر تعداد دستورات شرطی بسیار
زیاد باشد کاراتر است.
در این مثال شما تنها از ترکیب دو عبارت استفاده کردید. ولی هیچ محدودیتی برای تعداد عباراتی که ترکیب میکنید وجود ندارد.
مثال برای استفاده از عکس یک عبارت:
$ if [ ! -d $HOME/bin ] ; then mkdir $HOME/bin ; fi
بررسی شرط برقراری عبارت:
- در ابتدا d $HOME/bin- بررسی میشود
- در صورتی که عبارت صحیح باشد و دایرکتوری مورد نظر موجود باشد، مقدار بازگشتی توسط عبارات مرکب ۱ است. و در صورتی که موجود نباشد مقدار بازگشتی صفر است.
- در صورتی که نتیجهی مرحله قبل صحیح باشد(بازگشت مقدار ۰)، شرط if برقرار بوده و دستور then انجام میشود
شکل خلاصهتر این دستور به صورت زیر است:
$ test ! -d $HOME/bin && mkdir $HOME/bin
نتیجهی این دستور نیز مشابه حالت قبل است. زیرا دستور mkdir تنها در صورتی اجرا میشود که عبارت test صحیح باشد، و عبارت test تنها در صورتی صحیح است که دایرکتوری مورد نظر موجود نباشد.
مقایسهٔ رشتهها بهکمک ]]
صورت کلی:
[[ expression ]]
نکته: این علامت یک کلمهٔ کلیدی است، در حالیکه test یک دستور درونی بود:
$ type [ [ is a shell builtin $ type test test is a shell builtin $ type [[ [[ is a shell keyword
نکته: برای دیدن راهنمای این کلمهٔ کلیدی:
help [[
توجه: به دلایل فنی، لطفاً کدهای این قسمت را در شل کپی و پست نکنید، بلکه آنها را تایپ کنید.
مقایسهٔ رشتهها با هم
[[ STR1 OP STR2 ]]
که OP عملگر است و مقادیر آن:
< > == != >= <=
که البته = با == در این حالت یکی است
مثال:
$ [[ abc = abc ]] ; echo $? 0 $ [[ abc == abc ]] ; echo $? 0 $ [[ a < b ]] ; echo $? 0 $ [[ a < ab ]] ; echo $? 0 $ [[ 2 > 10 ]] ; echo $? 0
مقایسهٔ رشتهها با الگو(Pattern)
صورت کلی:
[[ STR == PATTERN ]] [[ STR != PATTERN ]]
مثال:
if [[ $FILE == *.png ]] ; then
برای تست کردن الگوها میتوانید از این فرمت استفاده کنید:
[[ image.png == *.png ]] ; echo $?
اگر 0 چاپ شد یعنی رشته با الگو منطبق میباشد، وگرنه منطبق نیست
مقایسهٔ رشتهها با Regular Expression
صورت کلی:
[[ STR =~ REGEXP ]]
در اینجا از POSIX Extended Regular Expression برای مقایسه استفاده میشود، برای اطلاعات بیشتر به اینجا مراجعه کنید.
چند مثال:
$ [[ image.png =~ .*\.png ]] ; echo $? 0 $ [[ image.png =~ *\.png ]] ; echo $? 2 $ [[ image.png =~ *.png ]] ; echo $? 2
مقایسهٔ عددی
مقایسهٔ عددی در این حالت کاملاً شبیه دستور test است، یعنی از این عملگرها استفاده میشود:
-eq -ne -lt -le -gt -ge
عبارتهای منطقی
راهنمای رسمی:
( EXPRESSION ) Returns the value of EXPRESSION ! EXPRESSION True if EXPRESSION is false; else false EXPR1 && EXPR2 True if both EXPR1 and EXPR2 are true; else false EXPR1 || EXPR2 True if either EXPR1 or EXPR2 is true; else false
روش بکار بردن عبارات منطقی در اینجا، مشابه زبانهایی مثل C و جاوا است، علامت && به معنای «و»، || به معنای «یا»، و ! به معنای نقیض عبارت است، پرانتز هم طبق نیاز در عبارتهای منطقی پیچیدهتر استفاده میشود.
عبارتهای حسابی و مقایسهٔ عددی با ))
راهنمای رسمی:
$ help '((' (( ... )): (( expression )) Evaluate arithmetic expression. The EXPRESSION is evaluated according to the rules for arithmetic evaluation. Equivalent to "let EXPRESSION". Exit Status: Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise.
چند مثال:
$ (( 2 < 10 )) ; echo $? 0
$ if (( 2 < 10 )) ; then echo "This is numeric comparision"; fi This is numeric comparision
$ (( 2*10 >= 19 )) ; echo $? 0
برای لیست کامل نوع عبارتهایی که میتوان در این حالت استفاده کرد:
help let
ساختار دستور case
دستور case یکی دیگر از کنترل کنندههای جریان در شل است. شکل کلی ساختار case به صورت زیر است:
case word in pattern1) list1 ;; pattern2) list2 ;; esac
در این ساختار word کلمهای است که با patternها مقایسه شده و در صورت
برابری با هر pattern، مجموعه دستورات list داخل آن pattern اجرا میشود. و
در زمانی که شرط برقرار شده و دستور list اجرا میشود علامت ;; در
پایان عبارت به این معنی است که کنترل جریان به انتهای ساختار case پرش
کند. چیزی مشابه دستور break در زبانهای همخانواده با سی.
همچنین برخی برنامه نویسان شکل کوتاه شدهی زیر را ترجیح میدهند:
case word in pattern1) list1 ;; pattern2) list2 ;; esac
البته از این حالت تنها در صورتی میتوان استفاده کرد که دستور list، یک دستور کوتاه باشد.
مثال:
FRUIT=kiwi case "$FRUIT" in apple) echo "Apple pie is quite tasty." ;; banana) echo "I like banana nut bread." ;; kiwi) echo "New Zealand is famous for kiwi." ;; esac
- در ابتدا داخل متغیر FRUIT رشته kiwi ذخیره میشود
- رشته kiwi با اولین الگو یعنی apple مقایسه میشود، و چون با الگو مطابقت ندارد به سراغ الگوی بعدی میرود
- رشته kiwi با دومین الگو یعنی banana مقایسه میشود، و چون با الگو مطابقت ندارد به سراغ الگوی بعدی میرود
- رشته kiwi با آخرین الگو یعنی kiwi مقایسه میشود، و چون با الگو مطابقت دارد دستور echo اجرا شده و عبارت .New Zealand is famous for kiwi بر روی صفحهی نمایش چاپ میشود.
استفاده از الگوها در ساختار case
در صورتی بخواهیم مثال قبل را با ساختار if پیاده سازی کنیم، نتیجه به صورت زیر میشود:
if [ "$FRUIT" = apple ] ; then echo "Apple pie is quite tasty." elif [ "$FRUIT" = banana ] ; then echo "I like banana nut bread." elif [ "$FRUIT" = kiwi ] ; then echo "New Zealand is famous for kiwi." fi
که دارای ساختار طولانیتری نسبت به حالت قبل است. اما قدرت اصلی ساختار
case کوتاه کردن ساختار if نیست. بلکه قدرت اصلی ساختار case در استفاده
از الگوها (patterns) نهفته است.
یک الگو رشتهای شامل عبارات منظم یا کلمات است. در الگوها میتوان از
جانشینهایی که در فصل هشت بررسی شدند استفاده نمود و میتوان دو یا چند
عبارت را با کاراکتر | با هم or کرد.
مثال:
case "$TERM" in *term) TERM=xterm ;; network|dialup|unknown|vt[0-9][0-9][0-9]) TERM=vt100 ;; esac
در این مثال مقدار متغیر TERM با دو الگو مورد مقایسه قرار میگیرد. در صورتی که مقدار این متغیر با رشتهی "term" پایان یابد دستور TERM=xterm انجام شده و در صورتی که این مقدار یکی از عبارات network یا dialup یا unknown و یا به صورت vtXXX (که XXX یک عدد سه رقمی است)باشد دستور TERM=vt100 انجام میشود.
Eman ۱۷ فوریهٔ ۲۰۱۲، ساعت ۱۱:۴۷ (UTC)