مقدمه
در درس گذشته با ساختار شرطی if، به اسکریپتهای خود قدرت تصمیمگیری بخشیدیم. حال به
یکی دیگر از مفاهیم بنیادی برنامهنویسی میپردازیم: حلقهها (Loops). اگر بخواهیم یک کار
مشخص را چندین بار تکرار کنیم، مثلاً نام ۱۰۰ فایل را تغییر دهیم یا به ۲۰۰ سرور متصل شویم، آیا
باید دستور مربوطه را به همان تعداد در اسکریپت خود کپی کنیم؟ قطعاً نه. حلقهها برای حل همین مشکل
به وجود آمدهاند.
یک حلقه، ساختاری است که به ما اجازه میدهد یک بلوک از کد را به صورت مکرر اجرا کنیم. این قابلیت،
کلید اصلی اتوماسیون در اسکریپتنویسی است. در شل، دو نوع حلقهی اصلی وجود دارد که هر کدام کاربرد
خاص خود را دارند:
- حلقهی for: برای تکرار روی یک لیست مشخص از آیتمها (مانند فایلها، کاربران
یا سرورها).
- حلقهی while: برای تکرار تا زمانی که یک شرط خاص برقرار باشد.
حلقهی for: تکرار روی یک لیست
کاربرد اصلی حلقهی for، اجرای یک سری دستور به ازای هر آیتم در یک لیست است. ساختار کلی
آن به شکل زیر است:
for variable in item1 item2 item3 ...
do
# commands to execute
# here we can use $variable
done
تکرار روی فایلها
یکی از قدرتمندترین کاربردهای حلقهی for، انجام عملیات روی مجموعهای از فایلها با
استفاده از wildcardها (مانند *) است. اسکریپت زیر تمام فایلهای با پسوند
.jpg در دایرکتوری فعلی را پیدا کرده و آنها را به یک دایرکتوری پشتیبان منتقل میکند.
#!/bin/bash
mkdir -p ./backups
for img_file in *.jpg
do
echo "Moving $img_file to backups directory..."
mv "$img_file" ./backups
done
echo "All JPEG files moved."
توجه کنید که چگونه نام فایل در هر بار تکرار حلقه، در متغیر img_file قرار میگیرد و ما
میتوانیم در بدنهی حلقه از آن استفاده کنیم.
حلقهی while: تکرار تا زمانی که شرط برقرار است
برخلاف حلقهی for که روی یک لیست متناهی اجرا میشود، حلقهی while یک بلوک
کد را تا زمانی که یک شرط خاص برقرار باشد، تکرار میکند. این حلقه از همان ساختار شرطی [[ ...
]] که در درس if یاد گرفتیم، استفاده میکند.
while [[ condition ]]
do
# commands to execute
done
یک شمارندهی ساده
مثال کلاسیک برای حلقهی while، یک شمارنده است. اسکریپت زیر اعداد ۱ تا ۵ را چاپ میکند:
#!/bin/bash
counter=1
while [[ $counter -le 5 ]]
do
echo "Count: $counter"
((counter++))
done
در این مثال، حلقهی while تا زمانی که مقدار متغیر counter کوچکتر یا مساوی ۵
باشد، ادامه پیدا میکند. خط ((counter++)) حیاتی است؛ این دستور که یک ساختار محاسباتی
در شل است، مقدار شمارنده را در هر بار تکرار یکی افزایش میدهد. اگر این خط وجود نداشت، شرط حلقه
همیشه برقرار بود و ما با یک حلقهی بینهایت مواجه میشدیم.
خواندن فایل به صورت خط به خط
یکی از رایجترین و مهمترین الگوهای استفاده از حلقهی while، خواندن و پردازش یک فایل
به صورت خط به خط است. این کار با ترکیب هوشمندانهی حلقهی while، دستور
read و تغییر مسیر ورودی انجام میشود.
اسکریپت زیر یک فایل به نام servers.txt را میخواند و به ازای هر خط (که نام یک سرور
است) یک پیام چاپ میکند:
#!/bin/bash
FILENAME="servers.txt"
if [[ ! -f "$FILENAME" ]]; then
echo "Error: File '$FILENAME' not found."
exit 1
fi
while IFS= read -r line
do
echo "Pinging server: $line ..."
ping -c 1 "$line"
done < "$FILENAME"
echo "Finished processing all servers."
کالبدشکافی حلقهی خواندن فایل
عبارت while IFS= read -r line یک اصطلاح استاندارد و بسیار قوی برای خواندن فایل در
شل است:
- done < "$FILENAME" : این بخش با استفاده از تغییر مسیر ورودی، محتوای
فایل $FILENAME را به ورودی استاندارد کل حلقهی while هدایت میکند.
- read -r line: دستور read از این ورودی استاندارد، یک خط
خوانده و آن را در متغیر line قرار میدهد. گزینهی -r از تفسیر
کاراکتر بکاسلش جلوگیری میکند. وقتی دیگر خطی برای خواندن وجود نداشته باشد،
read با کد خروج غیر صفر پایان مییابد و شرط while نقض شده و حلقه
تمام میشود.
- IFS=: این بخش کمی پیشرفتهتر است و برای جلوگیری از حذف ناخواستهی
فاصلهها و tabهای ابتدا و انتهای خط به کار میرود.