فصل یازدهم - حلقهها
در این فصل شما نحوهی تعریف و بکارگیری حلقهها در شل را خواهید آموخت.
حلقهها به شما اجازه میدهند تا یک تکه کد را به تعداد دفعات دلخواه اجرا
کنید. دو نوع کلی از حلقههایی که در این فصل بررسی میشوند حلقههای زیر
هستند:
- حلقه for
- حلقه while
حلقهی while این امکان را به شما میدهد تا مجوعهای از دستورات را تا زمانی که شرط حلقه برقرار است اجرا کنید.
و حلقهی for امکان اجرای دستورات را برای هر آیتم از لیست مورد نظر فراهم میسازد.
همچنین در این فصل علاوه بر این دو حلقه، با دو حلقهی دیگر به نام until و select نیز آشنا خواهید شد.
محتویات[نهفتن] |
حلقهی while
از این ساختار برای ایجاد حلقه های تکرار استفاده می شود. تا زمانیکه
دستور command عبارتی معتبر باشد مجموعه دستورات list اجرا خواهند شد و به
محض نادرستی شرط، کنترل دستورات از حلقه خارج خواهد شد.
ساختار کلی این حقله به صورت زیر است:
while command ; do list done
یا:
while command do list done
اگر هر دو دستور command و list دستورات کوتاه باشند به صورت زیر نیز میتوان حلقه را پیاده سازی کرد:
while command ; do list ; done
در اینجا command یک دستور اجرایی است، در حالی که list میتواند یک یا چند
دستور باشد، به مجموعه دستورات list بدنهی حلقه نیز گفته میشود. اگرچه
command میتواند هر دستور معتبری در شل باشد ولی اغلب برای command از یک
عبارت test استفاده میشود.
روند اجرای حلقه while در شل به صورت زیر است:
- اجرای command
- اگر مقدار برگشت داده شده توسط آن غیر صفر باشد، از حلقه خارج میشود
- در غیر اینصورت مجموعه دستورات list انجام میشود
- پس از اجرای همه دستورات list به مرحله اول برمیگرد
در واقع شرط ادامه یافتن حلقه while بازگشت مقداری غیر صفر پس از اجرای دستور command است به این معنی که دستور command با موفقیت انجام نشده است.
مثال:
حلقهی زیر را در نظر بگیرید:
x=0 while [ $x -lt 10 ] ; do echo $x x=$[x+1] done
خروجی این حلقه به صورت زیر است:
0 1 2 3 4 5 6 7 8 9
هر بار که حلقه اجرا میشود، مقدار متغیر x با مقدار 10 مقایسه میشود، در صورتی از ۱۰ کوچکتر باشد، بدنهی حلقه اجرا میشود. در بدنهٔ حلقه x چاپ شده و سپس یک واحد به آن اضافه میشود. به محض اینکه مقدار x بزرگتر یا برابر با ۱۰ شود اجرای حلقه خاتمه مییابد.
حلقههای تودرتو
در شل امکان استفاده از یک حلقه در داخل حلقه دیگر نیز امکان پذیر است:
while command1 ; do # this is loop1, the outer loop list1 while command2 ; do # this is loop2, the inner loop list2 done list3 done
در اینجا به حلقهی loop1، حلقهی اصلی یا حلقهی خارجی و به حلقهی loop2 حلقهی داخلی گفته میشود.
شل در تعداد حلقههایی که میخواهند به صورت تودرتو استفاده شوند، هیچ
محدودیتی اعمال نمیکند و شما میتوانید از هر تعداد حلقه را به صورت تودرتو
مورد استفاده قرار دهید.
مثال:
x=0 while [ "$x" -lt 10 ] ; do # this is loop1 y="$x" while [ "$y" -ge 0 ] ; do # this is loop2 echo -e "$y \c" y=$[y-1] done echo x=$[x+1] done
خروجی این برنامه به صورت زیر است:
0 1 0 2 1 0 3 2 1 0 4 3 2 1 0 5 4 3 2 1 0 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
کاربرد حلقه while برای تایید یک ورودی معتبر از کاربر
معمولا برای تعیین یک ورودی معتبر و تایید آن مراحل زیر طی میشود:
- از کاربر یک سوال پرسیده میشود
- پاسخ از کاربر دریافت میشود
- در صورت معتبر بودن پاسخ تایید خواهد شد و در غیر اینصورت مرحله اول تکرار میشود
بهترین ابزار برای شبیه سازی این کاربرد حلقهی while است. برای پیاده سازی
مراحل بالا با حلقه while میتوان از الگوریتم زیر استفاده نمود:
- یک متغیر تعریف و مقدار آن را تهی قرار میدهیم
- یک حلقه while تعریف و شرط پایان آن را غیر تهی بودن متغیر قرار میدهیم
- در بدنهی حلقه از کاربر یک سوال پرسیده و مقدار ورودی را میخوانیم
- مقدار ورودی را ارزیابی میکنیم
- در صورت معتبر بودن ورودی متغیر با ورودی مقدار دهی میشود و در غیر اینصورت مقدار متغیر تهی میماند
- در صورت مقدار گرفتن متغیر حلقه پایان میابد و در غیر اینصورت بدنه حلقه مجددا اجرا میشود
مثال: مراحل بالا میتوانند به صورت زیر پیاده سازی شوند:
while read -p "Enter the name of a directory where your files are located: " RESPONSE ; do if [ -d "$RESPONSE" ] ; then break else echo "ERROR: Please enter a directory pathname." fi done
اشاره: دستور read که در این مثال برای خواندن از ورودی به کارگرفته شده است در فصل ۱۳ بررسی شدهاست.
حلقه until
ساختار این حلقه دقیقا مشابه حلقه while و به صورت زیر است:
until command ; do list done
یا
until command do list done
تنها تفاوت این حلقه با حلقه while شرط اتمام آن است. شرط پایان یافتن حلقهی until اجرای موفقیت آمیز دستور command و برگشت کد صفر توسط آن است.
مثال:
حلقهی while زیر را در نظر بگیرید:
x=1 while [ ! $x -ge 10 ] ; do echo $x x=$[x+1] done
که با استفاده از حلقهی until میتوان آنرا به صورت زیر پیاده سازی کرد:
x=1 until [ $x -ge 10 ] ; do echo $x x=$[x+1] done
حلقهی until هیچ مزیت خاصی نسبت به حلقهی while ندارد و معمولا در بین برنامه نویسها معمولا محبوب نیست و به همین دلیل از آن استفادهی چندانی نمیکنند.
حلقهی for
برخلاف حلقهی while که تنها در یک شرایط خاص از حلقه خارج میشد، حلقهی for برای پیمایش لیستی از آیتمها مورد استفاده قرار میگیرد.
ساختار کلی حلقهی for به صورت زیر است:
for name in word1 word2 ... wordN ; do list done
یا
for name in word1 word2 ... wordN do list done
و شکل کوتاه شدهی آن به صورت زیر است:
for name in word1 word2 ... wordN ; do list ; done
در اینجا name متغیر و word1 تا wordN آیتمهایی هستند که با فاصله از
هم جدا میشوند. در هر بار اجرای حلقهی for متغیر name برابر با یکی از
word1 تا wordN میشود. به صورتی که در اجرای اول برابر word1 در اجرای دوم
برابر word2 و در اجرای آخر برابر wordN میشود. در واقع تعداد دفعاتی که
بدنهی حلقهی for اجرا میشود برابر تعداد کلمات است.
مثال:
for i in 0 1 2 3 4 5 6 7 8 9 ; do echo $i done
یا
for i in {0..9} ; do echo $i done
یا
for i in `seq 0 9` ; do echo $i done
یا
for ((i=0;i<=9;i++)) ; do echo $i done
که همه منجر به تولید خروجی زیر میشود:
0 1 2 3 4 5 6 7 8 9
توجه کنید که هیچ به اجباری به پشت سر بودن اعداد یا کلمات با ترتیب خاص نیست.
مثال:
for i in 0 1 2 4 3 5 8 7 9 ; do echo $i done
که منجر به تولید خروجی زیر میشود:
0 1 2 4 3 5 8 7 9
استفاده از حلقهی for برای دستکاری چندین فایل
فرض کنید میخواهید چندین فایل را در یک دایرکتوری کپی و مجوز دسترسی آنها را تغییر دهید. در اینصورت باید شما به صورت دستی هر فایل را کپی و سطح دسترسی آنرا تغییر دهید. راه حل بهتر از حلقهی for برای این منظور است. برای پیاده سازی این عمل با حلقهی for روند زیر را باید طی کنیم:
- یک حلقهی for با متغیر حلقه به نام file یا FILE ایجاد میکنیم
- یک لیست از فایلها تهیه میکنیم. همچنین میتوان از روش جانشینی نام که در فصل ۸ بررسی شد برای ایجاد لیست استفاده کنیم
- در بدنهی حلقه دستکاری مورد نظر بر روی متغیر حلقه اعمال میکنیم
مثال:
for FILE in $HOME/.bash* ; do cp $FILE ${HOME}/public_html chmod a+r ${HOME}/public_html/${FILE} done
در این مثال تمام فایلهایی که در دایرکتوری خانه قرار دارند و اسم آنها با bash. شروع میشود، در دایرکتوری public_html موجود در دایرکتوری خانه کپی شده و مجوز خواندن برای همه گروهها به آنها داده میشود.
حلقهی select
یک راه ساده برای ایجاد منوی شماره گذاری شده که کاربر میتواند از بین
گزینههای موجود انتخاب کند استفاده از حلقهی select است. استفاده از این
حلقه زمانی مفید است که شما نیاز دارید تا از کاربر سوالی پرسیده شود و
کاربر از بین یک یا چند گزینه که شما مشخص کردهاید انتخاب کند.
شکل کلی حلقهی select مشابه حلقهی for و به صورت زیر میباشد:
select NAME in word1 word2 ... wordN ; do list done
در اینجا NAME متغیر حلقه و word1 تا wordN دنبالهای از کلمات است که بوسیلهی فاصله از هم جدا میشوند.
روند کلی اجرای حلقهی select به صورت زیر است:
- در ابتدا هر کدام از آیتمها به همراه یک شماره نمایش داده میشوند
- در این حالت معمولا به صورت ?# است و منتظر دریافت ورودی از کاربر میماند
- زمانی که کاربر یک مقدار را وارد میکند، متغیر REPLAY$ با آن مقدار دهی میشود
- در صورتی که مقدار REPLAY یکی از عددهای نمایش داده شده برای آیتمها باشد آن آیتم انتخاب میشود، و در غیر اینصورت مجددا لیست شماره گذاری شده نمایش داده میشود
- مطابق عددی که در مرحلهی قبل انتخاب شد، یک عمل مشخص انجام میشود
- در صورتی که انجام عملیات در مرحلهی قبل موجب خروج از حلقه نشود کنترل حلقه مجددا به مرحلهی یک میرود
مثال
select COMPONENT in comp1 comp2 comp3 all none ; do case $COMPONENT in comp1|comp2|comp3) CompConf $COMPONENT ;; all) CompConf comp1 CompConf comp2 CompConf comp3 ;; none) break ;; *) echo "ERROR: Invalid selection, $REPLY." ;; esac done
خروجی این برنامه به صورت زیر است:
1) comp1 2) comp2 3) comp3 4) all 5) none #?
علامت ?# به این معنی است که شل منتظر دریافت یک ورودی از کاربر
است. که با توجه به هر عددی که کاربر وارد کند عمل خاصی طبق کد بالا انجام
میشود.
نکته: با استفاده از متغیر PS3 میتوان در این حالت شکل prompt را در هنگام دریافت ورودی از حالت ?# به هر حالت دلخواه دیگر تغییر داد.
میتوان در مثال قبل کد زیر را به ابتدای برنامه اضافه کرد:
$ export PS3="Please make a selection => "
در این صورت پس از اجرای برنامه خروجی زیر حاصل میشود:
1) comp1 2) comp2 3) comp3 4) all 5) none Please make a selection =>
کنترل حلقهها
در هنگام کار با حلقهها گاهی نیاز میشود تا روند اجرای حلقه را تغییر دهیم. با استفاده از دو دستور break و continue میتوان روند اجرای حلقه را تغییر داد. هر کدام از این دو دستور در ادامه بررسی شدهاند.
دستور break
این دستور برای پایان دادن به یک حلقه بدون در نظر گرفتن شرط حلقه
استفاده میشود. در واقع هر جا از دستور break استفاده شود، کنترل برنامه
به خارج حلقه منتقل میشود.
مثال:
حلقهی نامتناهی زیر را نظر بگیرید:
while : do read CMD case $CMD in [qQ]|[qQ][uU][iI][tT]) break ;; *) process $CMD ;; esac done
در این مثال به دلیل اینکه شرط حلقه همیشه صحیح است، یک حلقه نامتناهی
است و به طور معمول کنترل برنامه هیچگاه از حلقه خارج نمیشود. در این حلقه
تنها زمانی از حلقه خارج میشود که ورودی کاربر یکی از کاراکترهای q,Q یا
کلمهی quit (بدون درنظر گرفتن بزرگی یا کوچکی حروف) باشد، در اینصورت
دستور break اجرا شده و از حلقه خارج میشود.
همچنین برای دستور break میتوان یک عدد صحیح و مثبت بزرگتر از یک را
به عنوان آگومان مشخص کرد. این آرگومان مشخص کنندهی سطح پرش از حلقهها
تودرتو است. به عبارت دیگر اگر چندین حلقه به صورت تودرتو داشته باشیم این
آرگومان مشخص میکند که چند سطح به طرف بیرونی ترین حلقه پرش کنیم.
مثال:
اسکریپت زیر را که برای تهیه پشتیبان میتواند مورد استفاده قرار بگیرد را در نظر بگیرید:
for i in 1 2 3 4 5 ; do mkdir -p /mnt/backup/docs/ch0${i} if [ $? -eq 0 ] ; then for j in doc c h m pl sh do cp $HOME/docs/ch0${i}/*.${j} /mnt/backup/docs/ch0${i} if [ $? -ne 0 ] ; then break 2 ; fi done else echo "Could not make backup directory." fi done
چون در این برنامه دو حلقه تودرتو داریم و آرگومان دستور break نیز برابر ۲ است، اجرای دستور break 2 موجب منتقل شدن کنترل برنامه به خارج از هر دو حلقه میشود.
دستور continue
این دستور مشابه دستور break است با این تفاوت که به جای خروج از کل
حلقه تنها از یکبار اجرای حلقه جلوگیری میکند. استفاده از این دستور زمانی
میتواند مفید باشد که در هنگام اجرای حلقه مشکلی پیش آمده ولی قصد خروج از
کل حلقه را نداریم و تنها نیاز داریم تا از یکبار اجرای حلقه پرش کنیم.
مثال
for FILE in $FILES ; do if [ ! -f "$FILE" ] ; then echo "ERROR: $FILE is not a file." continue fi # process the file done
در این مثال هر گاه متغیر FILE حاوی نام یک فایل نباشد، تنها اجرای همان حلقه لغو میشود. و کنترل برنامه مجددا به ابتدای حلقه منتقل میشود.
Eman ۱۷ فوریهٔ ۲۰۱۲، ساعت ۱۱:۴۷ (UTC)