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

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

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

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

پرسش و پاسخ شماره ۸۹

پرسش و پاسخ شماره ۸۹

من در حال خواندن سطر به سطر یک فایل, و اجرای ssh یا ffmpeg هستم، فقط سطر اول پردازش می‌شود!

خواندن سطر به سطر فایل، اگر یک دستور در داخل حلقه نیز stdin را بخواند، می‌تواند فایل ورودی را تهی کند، برای مثال:

  # مثالی که کار نمی‌کند‎
  while IFS= read -r file; do
    ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv
  done < <(find . -name '*.avi')

  # مثالی که کار نمی‌کند‎
  while read host; do
    ssh "$host" some command
  done <hostslist

در اینجا چه اتفاقی رخ می‌دهد؟ اجازه بدهید مثال اول راببینیم. read سطری از ورودی استاندارد(‎FD 0‎) می‌خواند،آن را در پارامتر file قرار می‌دهد، و بعد ffmpeg اجرا می‌شود. مانند هر برنامه دیگری که شما از BASH اجرا می‌کنید، ffmpeg نیز ورودی استاندارد را به ارث می‌برد، که بنا به دلایلی از آن می‌خواند. من نمی‌دانم چرا. اما در هر وضعیتی، موقعی که ‎ffmpeg از stdin‎ می‌خواند، تمام ورودی را از فرمان find جذب می‌کند، حلقه از گرسنگی می‌میرد.

این هم چگونه کارآمد ساختن آن:

  while IFS= read -r file; do
    ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null
  done < <(find . -name '*.avi')

به تغییر مسیر در سطر ffmpeg توجه نمایید: ‎</dev/null‎. مثال ssh می‌تواند به همین ترتیب ، یا با گزینه ‎-n‎ اصلاح بشود (حداقل با OpenSSH).

گاهی اوقات شاید کار کردن با آنچه از ورودی استاندارد خوانده می‌شود، با حلقه‌های بزرگ دشوار گردد، یا ممکن است برنامه موقعی که شما ‎</dev/null را به آن اضافه می‌کنید رفتارش را تغییر بدهد. در این وضعیت شما می‌توانید کاری کنید که فرمان read از توصیف‌گر فایل متفاوتی استفاده کند، که کمتر محتمل است یک برنامه تصادفی از آن بخواند:

  while read <&3 line; do
    ......
  done 3<file

یا از گزینه ‎-u‎ فرمان read استفاده کنید(POSIX نیست):

  # Bash
  while read -u 3 line; do
    ......
  done 3<file


CategoryShell

پرسش و پاسخ 89 (آخرین ویرایش ‎2013-05-17 20:58:31‎ توسط GreyCat)