حل

حلقه‌زنی در Bash

{one: "۱ تمرین", other: "%{count} تمرین"}

درباره‌ی حلقه‌زنی

Bash دو شکل حلقه دارد:

  • تکرار چند دستور برای هر عنصر در فهرستی از عناصر،
  • تکرار چند دستور تا وقتی شرطی «درست» (یا «غلط») باشد.

حلقههای For

برای پیمایش فهرستی از عناصر، از حلقهی for استفاده میکنیم.

for varname in words; do COMMANDS; done

words با تقسیم واژه و بسط نام فایل باز میشود (که در مفهوم Quoting با آن آشنا شدیم). هر واژه به نوبت به varname نسبت داده میشود و COMMANDS اجرا میشوند.

در ادامه چند مثال میآوریم که نشان میدهند حلقهی For چه وقت به کار میآید

  • اجرای چند دستور روی گروهی از فایلها.

    # source all the bash library files in the current directory
    for file in ./*.bash; do
        source "$file"
    done
    
  • اجرای دستوری روی هر یک از پارامترهای یک اسکریپت یا تابع.

    for arg in "$@"; do
        echo "argument: ${arg}"
    done
    
  • اجرای دستوری برای هر واژهی جداشده با فاصله در خروجی یک دستور.

    for i in $(seq 10); do
        echo "${i} squared is $((i * i))"
    done
    

    بعداً دربارهی این سبک هشدارهایی خواهیم دید.

حلقهی For حسابی

Bash حلقهی حسابی هم دارد. این ساختار برای برنامهنویسانی که با زبانهای دیگر آشنا هستند تا حدی آشنا خواهد بود.

for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done

پرانتزهای دوتایی در Bash نشاندهندهی بافت حسابی هستند (در مفهومی دیگر دربارهی محاسبات در Bash بیشتر خواهیم دید). مثال بالا که از seq استفاده میکند را میتوان اینگونه نوشت

for ((i = 1; i <= 10; i++)); do
    echo "${i} squared is $((i * i))"
done

چه وقت از For استفاده نکنیم

خطهای یک فایل را با for نخوانید.

این یک ضدالگو است که زیاد میبینید: پیمایش خروجی cat.

for line in $(cat some.file); do 
    do_something_with "$line"
done

این کار از دو جهت اشتباه است.

۱. جانشینی دستور بدون نقلقول است، پس در معرض تقسیم واژه قرار میگیرد. تقسیم واژه بهطور پیشفرض روی فاصله انجام میشود، نه فقط روی خطهای جدید. این حلقهی For روی واژههای فایل پیمایش میکند، نه روی خطها. ۲. جانشینی دستور بدون نقلقول است، پس در معرض بسط نام فایل قرار میگیرد. هر واژه در فایل بهعنوان یک الگوی glob تطبیق داده میشود.

همانطور که در مفهوم Quoting آموختیم، تقسیم واژه را میتوان با متغیر IFS کنترل کرد و بسط نام فایل را میتوان خاموش کرد. اما این راهحل شکنندهای است. for روش درستی برای پیمایش خطهای یک فایل نیست. روش اصطلاحی خواندن یک فایل، استفاده از حلقهی while است.

حلقههای While

از while استفاده کنید تا دنبالهای از دستورها را تا وقتی شرطی «درست» است تکرار کنید.

while CONDITION_COMMANDS; do COMMANDS; done

همانند if که در مفهوم Conditionals آموختیم، نحو خاصی برای CONDITION_COMMANDS وجود ندارد. وضعیت خروج فهرست دستورها تعیین میکند که «درست» است یا «غلط».

کنترل حلقهها

دستور break از حلقه بیرون میزند. اجرا با دستوری که پس از پایاندهندهی done حلقه میآید ادامه مییابد.

دستور continue به تکرار بعدی حلقه میرود.

خواندن خطهای یک فایل

همانطور که گفته شد، حلقهی While روش اصطلاحی خواندن یک فایل است.

while IFS= read -r line; do
    do_something_with "$line"
done < some.file
  • محتوای فایل با تغییرمسیر < بهعنوان ورودی به حلقه داده میشود.

  • دستور read اگر بتواند یک خط کامل از ورودیاش بخواند، «درست» برمیگرداند. وقتی ورودی مصرف شود، یا اگر آخرین خط بدون خط جدید پایانی تمام شود، read مقدار «غلط» برمیگرداند.

    • روش واقعاً اصطلاحی برای خواندن خطهای یک فایل، حتی اگر آخرین خط با خط جدید تمام نشود، این است

      while IFS= read -r line || [[ -n "$line" ]]; do ...
      
  • گزینهی -r به read میگوید که دنبالههای بکاسلش را جایگزین نکند: بکاسلش فقط یک نویسهی معمولی است.

  • IFS= رشتهی خالی را فقط برای مدت اجرای دستور read به IFS نسبت میدهد. این کار تقسیم واژه را موقتاً خاموش میکند تا فاصلههای ابتدا یا انتهای خط ورودی حفظ شوند.

Until

ساختار until دنبالهای از دستورها را تا وقتی شرطی «غلط» است تکرار میکند. حلقه تا زمانی که شرط «درست» شود تکرار میشود.

until CONDITION_COMMANDS; do COMMANDS; done

استفاده از until کم پیش میآید. رایجتر این است که از یک حلقهی «while not» استفاده شود.

while ! CONDITION_COMANDS; do ...

حلقههای بینهایت

گاهی میخواهید تا بینهایت حلقه بزنید. دو راه برای این کار وجود دارد.

۱. یک حلقهی While با شرطی که همیشه وضعیت خروج موفق دارد

```bash
while true; do ...
```

۲. یک حلقهی For حسابی با عبارتهای خالی

```bash
for ((;;)); do ...
```

Do-While

ساختار صریح do-while وجود ندارد، اما میتوانید همان اثر را به دست آورید:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود