استفاده از "for" را امتحان نکنید. از یک حلقه while و فرمان read استفاده کنید:
while read -r line do echo "$line" done < "$file"
کاربرد گزینه -r با فرمان read از تفسیر کاراکتر \ پیشگیری میکند(به طور معمول از این کاراکتر و کاراکتر سطر جدید برای ادامه یک سطر در سطر بعدی استفاده میشود). بدون این گزینه هر کاراکتر \ در ورودی، دور انداخته میشود. شما همیشه با فرمان read از گزینه -r استفاده کنید.
line نام متغیر انتحاب شده توسط شما میباشد. به جای آن هر نام معتبر در پوسته را میتوانید قرار بدهید.
تغییر مسیر < "$file" به حلقه while میگوید، از فایلی که نام آن در متغیر file قرار دارد، بخواند. اگر شما ترجیح بدهید از یک نام مسیرکامل به جای متغیر استفاده کنید، میتوانید آن را نیز به کار ببرید. اگر منبع ورودی شما ورودی استاندارد اسکریپت باشد، در آنصورت شما به هیچوجه نمیتوانید از تغییر مسیر استفاده کنید
اگر منبع ورودی شما محتویات یک متغیر/پارامتر باشد، BASH میتواند با استفاده از "here string" تکرار روی سطرهای آن را انجام بدهد:
while read -r line; do echo "$line" done <<< "$var"
همین کار در هر پوسته گروه بورن با استفاده از "here document" قابل انجام است( گرچه read -r مربوط به POSIX است، نه Bourne):
while read -r line; do echo "$line" done <<EOF $var EOF
اگر مایل به لغو سطرهای توضیح شروع شده با کاراکتر # میباشید، میتوانید بهسادگی در داخل یک حلقه پرش از روی آن سطرها را انجام بدهید:
# Bash while read -r line do [[ $line = \#* ]] && continue echo "$line" done < "$file"
اگر میخواهید روی فیلدهای منحصربهفرد هر سطر عمل کنید، میتوانید متغیرهای اضافی برای read فراهم نمایید:
# Input file has 3 columns separated by white space. while read -r first_name last_name phone; do ... done < "$file"
اگر جداکننده فیلد، فضای سفید نباشد، میتوانید متغیر IFS (جداکننده فیلد درونی) را تنظیم کنید:
while IFS=: read -r user pass uid gid gecos home shell; do ... done < /etc/passwd
برای فایلهایی که جداکننده فیلد آنها Tab میباشد، IFS=$'\t' را به کار ببرید.
لزوماً احتیاجی به دانستن تعداد فیلدهای موجود در هر سطر ورودی ندارید. اگر تعداد متغیرهای بیشتری نسبت به فیلدهای موجود فراهم کنید، متغیرهای اضافی تهی خواهند شد. اگر متغیرهای کمتری فراهم نمایید، آخرین متغیر تمام فیلدهای باقیمانده را دریافت میکند. برای مثال،
read -r first last junk <<< 'Bob Smith 123 Main Street Elk Grove Iowa 123-555-6789' # first will contain "Bob", and last will contain "Smith". # junk holds everything else.
بعضی اشخاص از متغیر دورانداختنی _ به عنوان«متغیر ناخواسته» برای صرفنظر از فیلدها استفاده میکنند. این(یا در حقیقت هر متغیری) در صورتیکه دلواپس چیزی که در آن قرار میگیرد، نباشیم، میتواند بیش از یکبار در یک دستور منفرد read استفاده گردد:
read -r _ _ first middle last _ <<< "$record" #.از دو فیلد اول عبور میکنیم، سپس سه فیلد بعد را میخوانیم #.به خاطر داشته باشید _ انتهایی هر تعداد فیلد را جذب میکند #.نیازی نیست در آنجا تکرار بشود
فرمان read به طور پیشفرض هر سطر خوانده را با زدودن تمام کاراکترهای فضای سفید قبل و بعد ویرایش میکند(فاصلهها و tabها، یا هر کاراکتر فضای سفید معرفی شده درمتغیر IFS). اگر این کار مطلوب نباشد، باید متغیر IFS پاک بشود:
# Exact lines, no trimming while IFS= read -r line do printf '%s\n' "$line" done < "$file"
کسی ممکن است به جای یک فایل معمولی از یک فرمان بخواند:
some command | while read -r line; do other commands done
این شیوه مخصوصاً برای پردازش خروجی find با یک بلوک از دستورها، مفید است:
find . -type f -print0 | while IFS= read -r -d '' file; do mv "$file" "${file// /_}" done
این کُد هر دفعه یک فایل را از فرمان find میخواند و تغییر نام فایل را با تعویض کاراکترهای فاصله با خط زیر، انجام میدهد.
به کاربرد -print0 در فرمان find توجه نمایید، که از بایت تهی به عنوان جداکننده نام فایلها استفاده میکند، و -d '' در فرمان read به آن دستور میدهد که تمام متن را تا رسیدن به بایت تهی در متغیر file بخواند. به طور پیشفرض، find و read ورودی خود را با سطر جدید جدا میکنند، به هرحال، چون نام فایلها به طور بالقوه خودشان میتوانند محتوی سطرجدید باشند، این رفتار پیشفرض آن نام فایلها را در محل سطرجدید از هم جدا میکند و موجب نقص در بدنه حلقه میگردد. بعلاوه لازم است متغیر IFS به یک رشته تهی تنظیم شود، زیرا در غیر اینصورت read باز هم فضای سفیدهای قبل و بعد نامها را از بین میبرد. برای توضیحات تفصیلی FAQ #20 را ملاحظه نمایید.
استفاده از یک لوله برای ارسال خروجی find به داخل یک حلقه while ، حلقه را در پوسته فرعی قرار میدهد و بنابراین بعداً، موقعی که دستورات درون بدنه حلقه سعی در تنظیم متغیرهایی نمایند که لازم است پس از آن در حلقه به کار بروند، ممکن سبب مشکلاتی بشود، برای آن وضعیت، FAQ 24 را ملاحظه کنید، یا از جایگزینی پردازش استفاده کنید، مانند:
while read -r line; do other commands done < <(some command)
اگر میخواهید سطرهای فایل را به درون یک آرایه بخوانید، FAQ 5 را ببینید.
اگر بعد از آخرین سطر فایل، کاراکترهایی وجود داشته باشد(یا آن را به طور متفاوتی قرار دهید، اگر آخرین سطر با کاراکتر سطر جدید خاتمه نیافته باشد)، آنوقت فرمان read آن را میخواند اما غلط برمیگرداند، سطر شکسته و ناتمام درمتغیر(های) read رها میکند. شما بعد از حلقه میتوانید این سطر را پردازش کنید:
# cat شبیه سازی while IFS= read -r line do printf '%s\n' "$line" done < "$file" [ -n "$line" ] && printf %s "$line"
یا:
# :این کار نمیکند printf 'line 1\ntruncated line 2' | while read -r line; do echo $line; done # :این یکی هم کار نمیکند printf 'line 1\ntruncated line 2' | while read -r line; do echo "$line"; done; [[ $line ]] && echo -n "$line" # :این کار میکند printf 'line 1\ntruncated line 2' | (while read -r line; do echo "$line"; done; [[ $line ]] && echo "$line")
برای بحث در باره آنکه چرا دومین مثال فوق آنطور که انتظار دارید کار نمیکند،پرسش وپاسخ ۲۴ را ملاحظه نمایید.
برخی فرمانها آزمند خوردن تمام داده موجود در ورودی استاندارد هستند. مثالهای فوق اقدامات احتیاطی در قبال اینگونه برنامه ها نمیکنند. برای مثال،
while read -r line do cat > ignoredfile echo "$line" done < "$file"
فقط محتویات سطر اول را چاپ میکند، مابقی محتویات به "ignoredfile" میروند، چون cat تمام ورودی موجود را میمکد.
یک عبور موقت از این امر استفاده از توصیفگر فایل عددی به جای ورودی استاندارد است:
# Bash در پوسته while read -r -u9 line do cat > ignoredfile echo "$line" done 9< "$file"
یا:
# Bourne در پوسته exec 9< "$file" while read line <&9 do ... done exec 9<&-
این مثال، در هر تکرار به جای خوردن تمام ورودی حلقه، منتظر میماند تا کاربر چیزی درفایل ignoredfile تایپ نماید.
شاید شما احتیاج به این داشته باشید، برای مثال به mencoder که اگر ورودی کاربر وجود داشته باشد، آن را خواهد پذیرفت، اما اگر موجود نباشد به طور بیصدا ادمه خواهد داد. دستورات دیگری که به این طریق عمل میکنند از جمله ssh و ffmpeg هستند. عبور موقتهای اضافی برای این مورد در FAQ #89 بحث گردیدهاند.
پرسش و پاسخ 1 (آخرین ویرایش 2012-03-22 11:40:30 توسط 84-73-54-61)