Bash دو شکل حلقه دارد:
برای پیمایش فهرستی از عناصر، از حلقهی 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
بعداً دربارهی این سبک هشدارهایی خواهیم دید.
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 نخوانید.
این یک ضدالگو است که زیاد میبینید: پیمایش خروجی cat.
for line in $(cat some.file); do
do_something_with "$line"
done
این کار از دو جهت اشتباه است.
۱. جانشینی دستور بدون نقلقول است، پس در معرض تقسیم واژه قرار میگیرد. تقسیم واژه بهطور پیشفرض روی فاصله انجام میشود، نه فقط روی خطهای جدید. این حلقهی For روی واژههای فایل پیمایش میکند، نه روی خطها. ۲. جانشینی دستور بدون نقلقول است، پس در معرض بسط نام فایل قرار میگیرد. هر واژه در فایل بهعنوان یک الگوی glob تطبیق داده میشود.
همانطور که در مفهوم Quoting آموختیم، تقسیم واژه را میتوان با متغیر IFS کنترل کرد و بسط نام فایل را میتوان خاموش کرد.
اما این راهحل شکنندهای است.
for روش درستی برای پیمایش خطهای یک فایل نیست.
روش اصطلاحی خواندن یک فایل، استفاده از حلقهی 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 CONDITION_COMMANDS; do COMMANDS; done
استفاده از until کم پیش میآید.
رایجتر این است که از یک حلقهی «while not» استفاده شود.
while ! CONDITION_COMANDS; do ...
گاهی میخواهید تا بینهایت حلقه بزنید. دو راه برای این کار وجود دارد.
۱. یک حلقهی While با شرطی که همیشه وضعیت خروج موفق دارد
```bash
while true; do ...
```
۲. یک حلقهی For حسابی با عبارتهای خالی
```bash
for ((;;)); do ...
```
ساختار صریح do-while وجود ندارد، اما میتوانید همان اثر را به دست آورید:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done