آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

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

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

چرا سطرهای فایل را با "for" نخوانید

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

اول، روش درست:

    $ cat afile
    ef gh
    
    *
    $ while IFS= read -r aline; do echo "$aline" ; done < afile
    ef gh
    
    *

حال، سعی در استفاده از for:

    $ for i in $(<afile); do echo "$i"; done
    ef
    gh
    afile
    anotherfile
    stillanotherfile
    the_embarrassing_file_you_forgot_about

همانطور که می‌توانید ببینید، این کوشش برای تکثیر نمودن cat از چند جهت شکست می‌خورد. اول، سطر شامل دو کلمه به دو سطر خروجی تجزیه شده است. دوم، سطر خالی به کلی از قلم افتاده است. سوم، سطر شامل یک glob به نام تمام فایلهای شما(یکی در هر سطر) بسط یافته .

می‌توانیم از تجزیه سطر اول، با تنظیم متغیرIFS عبور کنیم، ومی‌توانیم با کاربرد گزینه ‎ -f‎ از بسط یافتن glob پیش‌گیری نماییم:

    $ IFS=$'\n'; set -f; for i in $(<afile); do echo "$i"; done; set +f; unset IFS
    ef gh
    *

توجه نمایید که حالا ترکیب دستوری، طولانی‌تر از آن حلقه while است -- و ما باز هم سطر خالی را از دست داده‌ایم! به طوری که در IFS و در FAQ #5 بحث شد،استفاده از‎ IFS=$'\n'‎ (یا هر فضای سفید دیگر در IFS) باعث می‌گردد، شل تمام فضاهای سفید متوالی جداکننده را یگانه‌سازی و به یکی تبدیل کند. به بیان دیگر، از روی سطرهای خالی عبور می‌کند.

هیچ راه گریزی برای این مورد نیست. اگر برای جداکردن سطرها به متغیر IFS تکیه کنید، نمی‌توانید سطرهای خالی را حفظ کنید.

موضوع دیگر با تنظیم IFS به این طریق، آنست که این متغیر در تمام طول بدنه حلقه، مقدارش را حفظ می‌کند. اگر بدنه حلقه شما ، پیچیده‌تر از ‎ echo "$i"‎ باشد، این مورد شاید نامطلوب باشد، چون شما یک IFS غیر استاندارد خواهید داشت، که احتمال دارد به غافل‌گیری ناخوش‌آیندی منجر گردد.

آخرین مسئله با خواندن سطرها از طریق حلقه for ناکارآمدی آن است. یک حلقه while read هر دفعه یک سطر از جریان ورودی را می‌خواند، ‎ $(<afile)‎ به یکباره تمام فایل را به داخل حافظه می‌بلعد. برای فایلهای کوچک، این مشکلی نیست، اما اگر در حال خواندن فایلهای بزرگ باشید، حافظه مورد نیاز، هنگفت خواهد بود. (الزاماً Bash مجبور خواهد بود، یک رشته برای نگهداری فایل و مجموعه دیگری از رشته‌ها برای نتایج حاصل از تفکیک کلمه اختصاص بدهد، حافظه اختصاص یافته دو برابر اندازه فایل ورودی خواهد بود.)

لطفاً پرسش و پاسخ شماره 1 را برای مثالهای بیشتر از چگونگی خواندن صحیح فایل، ملاحظه نمایید.


  • سطرها را با for نخوانید (آخرین ویرایش ‎ 2011-06-27 13:20:59 ‎توسط GreyCat)