منبع اصلی نوشتار زیر در این لینک قرار دارد

حلقه‌های شرطی

ادامه یادداشت قبل


راهنمای آموزشی  BashGuide   مؤلف  Lhunath

5. حلقه های شرطی( while و until و for)

تا اینجا آموخته‌اید چگونه برخی تصمیم‌گیری های اساسی در اسکریپت‌هایتان را بسازید. اگر چه، برای انجام همه انواع وظایفی که ممکن است از اسکریپت بخواهیم کافی نمی‌باشد. گاهی اوقات نیاز به تکرار برخی کارها داریم. برای همین، کاربرد یک حلقه لازم است. دو نوع اصلی ازحلقه( به اضافه نوع دیگری) وجود دارد، و استفاده از نوع صحیح حلقه به شما در نگهداری خوانایی و قابلیت پشتیبانی اسکریپت‌هایتان کمک می‌کند.

دو نوع اساسی حلقه‌ها while و for می‌باشند. حلقه while نوع دیگری دارد که until نامیده شده، که به سادگی بررسی را برعکس انجام می‌دهد، و حلقه for در دو قالب متفاوت ظاهرمی‌شود. در اینجا خلاصه‌ای از آنها:

  • while command: تا وقتی که command به طور موفقی اجرا می‌شود(کد خروج صفر است)، تکرار می‌شود.

  • until command: مادامی‌که command به طور ناموفق اجرا گردد(کد خروج صفر نباشد)، تکرار می‌شود.

  • for variable in words: حلقه برای هر یک از wordsکه به نوبت در متغیر variable قرار می‌گیرند، تکرار می‌شود.

  • for (( expression; expression; expression ))‎: با اجرای اولین عبارت حسابی شروع می‌کند، تا موقعیکه ارزیابی دومین عبارت حسابی موفق است حلقه تکرار می‌شود، و در پایان هر حلقه عبارت حسابی سوم انجام می‌شود.

هر شکل از حلقه‌ها با کلمه‌کلیدی do دنبال می‌شود، پس از آن یک یا چند فرمان در بدنه، بعد هم کلمه‌کلیدی done. کلمه‌کلیدی های do و done مشابه then و fi ( و elif یا else احتمالی) در ساختار دستور if که قبلاً دیدیم، می‌باشند. کار آنها این است که به ما بگویند حلقه از کجا شروع و به کجا ختم می‌شود.

در عمل، حلقه‌ها برای انواع مختلفی از وظایف به کار می‌روند. حلقه for (شکل اول) برای موقعی مناسب است که لیستی داریم، و می‌خواهیم عناصر آن را به طور ترتیبی به کار ببریم. حلقه while مناسب وقتی است که به طور دقیق نمی‌دانیم چند مرتبه عملی باید تکرار شود، و می‌خواهیم تا رسیدن به موردی که در انتظار آن هستیم تکرار انجام شود.

در اینجا چند مثال برای تشریح تفاوتها و همچنین شباهت‌های حلقه‌ها می‌آوریم. (یادآوری: در اکثر سیستم‌عامل‌ها، برای کشتن برنامه‌ای که در ترمینال در حال اجرا است از ترکیب کلیدی ‎Ctrl-C‎ استفاده می‌شود.)

    $ while true
    > do echo "Infinite loop"
    > done

    $ while ! ping -c 1 -W 1 1.1.1.1; do
    > echo "still waiting for 1.1.1.1"
    > sleep 1
    > done

    $ (( i=10 )); while (( i > 0 ))
    > do echo "$i empty cans of beer."
    > (( i-- ))
    > done
    $ for (( i=10; i > 0; i-- ))
    > do echo "$i empty cans of beer."
    > done
    $ for i in {10..1}
    > do echo "$i empty cans of beer."
    > done

سه حلقه آخری با ترکیب متفاوت، دقیقاً به نتیجه یکسانی می‌رسند. در تجربه اسکریپت‌نویسی شل خود بارها با این مورد مواجه می‌شوید. تقریباً همیشه راهکارهای چندگانه‌ای برای حل یک مسئله موجود است. به زودی تشحیص مهارت شما در حل مسئله نخواهد بود، آنقدر که درچگونگی بهترین روش حل آن خواهد بود. شما باید بیاموزید که بهترین زاویه رویکرد حل مسئله را انتخاب کنید. به طور معمول، سادگی و قابلیت انعطاف از عوامل اصلی کد نویسی خوب محسوب می‌شوند. مورد مطلوب شخصی من، آخرین مثال است. در آن نمونه من بسط ابرو را برای تولید کلمات به کار برده‌ام، اما روشهای دیگری نیز وجود دارد.

بیایید نگاه نزدیک‌تری به آن مثال آخری داشته باشیم، زیرا اگر چه از دو حلقه for به نظر آسان‌تر می‌آید، اما، اگر به طور دقیق ندانید که چگونه عمل می‌کند، غالباً می‌تواند فریب دهنده باشد.

به طوری که قبلاً اشاره کرده‌ام: for لیستی از کلمات را گرفته و هر یک از آنها را در متغیر شاخص حلقه می‌گذارد، هر دفعه یکی، و سپس بدنه حلقه را با آن اجرا می‌کند. قسمت فریب دهنده آن است که BASH چطور تصمیم می‌گیرد که کلمات کدام هستند. به من اجازه دهید خودم با بسط ابروها در مثال قبلی شرح بدهم:

    $ for i in 10 9 8 7 6 5 4 3 2 1
    > do echo "$i empty cans of beer."
    > done

BASH کاراکترهای بین کلمه‌کلیدی in و انتهای سطر را می‌گیرد، و آنها را به کلمات تفکیک می‌نماید. این تفکیک نسبت به فاصله و tabها انجام می‌شود، درست مانند تفکیک شناسه‌ها. اما اگر هر جایگزینی نقل‌قولی نشده‌ای آنجا باشد، آن هم به کلمات تفکیک می‌گردد(با استفاده از محتوای متغیر IFS). تمام این کلمات تفکیک شده، عناصر تکرار می‌شوند.

در نتیجه، خیلی مراقب باشید که اشتباه زیر را مرتکب نشوید:

    $ ls
    The best song in the world.mp3
    $ for file in $(ls *.mp3)
    > do rm "$file"
    > done
    rm: cannot remove `The\': No such file or directory
          rm: cannot remove `best\': No such file or directory
          rm: cannot remove `song\': No such file or directory
          rm: cannot remove `in\': No such file or directory
          rm: cannot remove `the\': No such file or directory
          rm: cannot remove `world.mp3\': No such file or directory

شما از قبل نسبت به نقل‌قولی کردن ‎$file‎ در دستور rm آگاه بودید، اما در اینجا چه چیزی اشتباه است؟ BASH جایگزینی دستور (‎$(ls *.mp3)‎) را بسط می‌دهد، آن را با خروجی دستور تعویض می‌کند، و بعد تفکیک کلمه را روی آن انجام می‌دهد(به علت آنکه نقل‌قولی نیست). در واقع Bash این عبارت را اجرا می‌کند for file in The best song in the world.mp3.
Boom, مات شدید.

خواهید گفت، آن را نقل‌قولی می‌کنم؟ اجازه دهید فایل دیگری اضافه کنم:

    $ ls
    The best song in the world.mp3  The worst song in the world.mp3
    $ for file in "$(ls *.mp3)"
    > do rm "$file"
    > done
    rm: cannot remove `The best song in the world.mp3  The worst song in the world.mp3\': No such file or directory

نقل‌قول‌ها به راستی از فضای سفید در نام فایل‌های شما محافظت می‌کنند، اما چیزی بیش از آن انجام می‌دهند. نقل‌قول‌ها از تمام فضاهای سفید خروجی فرمان ls محافظت خواهند کرد. راهی وجود ندارد که BASH بتواند تشخیص بدهد کدام بخشهای خروجی فرمان ls نام فایل‌ها را نمایندگی می‌کنند. خروجی فرمان ls یک رشته ساده است، و BASH با آن به همین عنوان رفتار می‌کند. بعد for تمام خروجی نقل‌قولی شده را در متغیر i قرار می‌دهد و دستور rm را با آن اجرا می‌کند. لعنت، دوباره مات شدید.

بنابراین چه کار بکنیم؟ به طوری که قبلاً پیشنهاد نمودم، جانشین‌ها بهترین دوست شما هستند:

    $ for file in *.mp3
    > do rm "$file"
    > done

حالا، BASH می‌داند که با نام فایل‌ها سروکار دارد، و نام فایها را می‌شناسد، و بنابراین به طور مطلوبی آنها راتفکیک می‌کند. نتیجه بسط جابشین چنین است: for ‎file in "The best song in the world.mp3" "The worst song in the world.mp3"‎. مشکل رفع شد!

حالا بیایید به حلقه while بپردازیم. حلقه while به خاطر ظرفیتش در اجرای دستورات تا موقع وقوع موضوع مورد نظر، خیلی جالب است. در اینجا چند مثال که نشان می‌دهد چگونه حلقه while غالباً به کار می‌رود :

    $ # ماشین نوشیدنی، نوشیدنی‌ها را در ازای بهای ۲۰ سنت تحویل می‌دهد
    $ while read -p $\'The sweet machine.\\nInsert 20c and enter your name: \' name
    > do echo "The machine spits out three lollipops at $name."
    > done

    $ # هر پنج دقیقه یکبار ایمیل شما را بررسی می‌کند
    $ while sleep 300
    > do kmail --check
    > done

    $ # برای برخط(آنلاین) شدن مجدد میزبان منتظر می‌ماند
    $ while ! ping -c 1 -W 1 "$host"
    > do echo "$host is still unavailable."
    > done; echo -e "$host is available again.\\a"

حلقه until خیلی به ندرت استفاده می‌شود، فقط به علت آنکه تا حد بسیار زیادی مشابه حلقه while  می‌باشد. می‌توانستیم آخرین مثال را با حلقه until به صورت زیربنویسیم:

    $ # برای برگشت میزبان به حالت آماده برقراری ارتباط منتظر می‌ماند
    $ until ping -c 1 -W 1 "$host"
    > do echo "$host is still unavailable."
    > done; echo -e "$host is available again.\\a"

در عمل، اکثر مردم حقیقتاً از حلقه while  به جای آن استفاده می‌کنند.

بالإخره، از دستور داخلی continue برای پرش به جلو بدون اجرای بقیه بدنه و اجرای دور بعدی تکرار حلقه، و دستور داخلی break برای پریدن به خارج از حلقه و ادامه دستورات پس از حلقه در اسکریپت، می‌توانید استفاده کنید. این دستورات با هر دو حلقه for و while کار می‌کنند.




  • حلقه: یک حلقه ساختاری است که برای تکرار کد تا موقع تحقق یک وضعیت معین، طراحی می‌شود. در آن نقطه حلقه متوقف می‌شود و کد بعد از آن اجرا می‌گردد.

  • for (کلمه‌کلیدی): یک حلقه for نوعی حلقه است که یک متغیر را به ترتیب، معادل عناصر لیستی از کمیت‌ها قرار می‌دهد، و بدنه را با آن متغیر اجرا می‌کند، و تا تمام شدن لیست تکرار می‌کند.

  • while (کلمه‌کلیدی): یک حلقه while نوعی حلقه است که اجرای کد را تا موقعی که یک دستور معین(قبل از هر تکرار اجرامی‌شود) به طور موفق اجرا می‌شود، ادامه می‌دهد.

  • until (کلمه‌کلیدی): یک حلقه until نوعی حلقه است که اجرای کد را تا موقعی که یک دستور معین(قبل از هر تکرار اجرامی‌شود) به طور ناموفق اجرا می‌شود، ادامه می‌دهد..


ادامه دارد ....