پرسش و پاسخ شماره ۹۵ - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

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

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

#!/bin/bash

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

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

میخواهم لیست خیلی بلند شناسه‌ها را دریافت کنم. چطور می‌توانم یک لیست بزرگ قابل توجه را پردازش نمایم؟

ابتدا، بیایید برخی موضوعات زمینه‌ای را بازبینی کنیم. موقعی که یک پردازش می‌خواهد پردازش دیگری را اجرا کند، یک فرزند ‎fork()‎(منشعب) می‌کند، و پردازش فرزند یکی از فراخوان‌های سیستمی خانواده ‎exec*‎ (مانند‎execve()‎)، را با دادن نام یا مسیر فایل برنامه پردازش جدید، نام پردازش جدید، لیست شناسه‌ها برای پردازش جدید، و در بعضی حالت‌ها، مجموعه‌ای از متغیرهای محیط، فراخوانی می‌کند. از این قرار:

  •  /* C */
     execlp("ls", "ls", "-l", "dir1", "dir2", (char *) NULL);

محدودیتی(به طور معمول) برای تعداد شناسه‌هایی که به این طریق می‌تواند عبور داده شود، وجود ندارد، اما در اکثر سیستم‌ها، حدی برای اندازه کل لیست، وجوددارد. برای جزئیات بیشتر، ‎http://www.in-ulm.de/~mascheck/various/argmax/‎ را ملاحظه کنید.

اگر شما در یک فراخوانی منفرد برنامه، سعی کنید (مثلاً) نام‌فایلهای بسیار زیادی را عبور بدهید، با موردی مشابه این مواجه می‌شوید:

  •  $ grep foo /usr/include/sys/*.h
     bash: /usr/bin/grep: Arg list too long

ترفندهای گوناگونی وجود دارد که می‌توانستید برای غلبه براین مورد به صورت تک موردی(فاقد عمومیت) به کار ببرید(اول تعویض دایرکتوری به ‎/usr/include/sys، و سپس استفاده از ‎grep foo *.h‎ برای کوتاه نمودن طول هر نام فایل...)، اما اگر راه‌کار کاملا نیرومندی لازم داشته باشید چطور؟

بعضی اشخاص در اینجا دوست دارند xargs را به کار ببرند، اما این مورد مسائل خطیری دارد. این فرمان با کاراکترهای فضای سفید و نقل‌قول در ورودی‌اش به عنوان جداکننده کلمات رفتار می‌کند، که آن را برای مدیریت صحیح نام‌فایلها، نالایق می‌سازد. (برای تشریح مطلب در این خصوص، صفحه کاربرد find را ببینید.)

اگر عمل بازگشتی مورد قبول است، می‌توانید به طور مستقیم find را استفاده کنید:

  •  find /usr/include/sys -name '*.h' -exec grep foo /dev/null {} +

اگر بازگشت قابل قبول نیست اما شما find گنو را دارید، می‌توانید این جایگزین غیرقابل‌حمل را به کار ببرید:

  • GNUfind /usr/include/sys -name '*.h' -maxdepth 1 -exec grep foo /dev/null {} +

(به یاد بیاورید که اگر grep بیش از یک نام فایل برای پردازش دریافت کند، تنها نام‌فایلها را چاپ خواهد کرد. پس، ما برای تضمین آنکه همواره حتی اگر ‎-exec‎ فقط یک نام به آن عبور دهد، حداقل دو نام‌فایل دارد، ‎/dev/null‎ را به عنوان نام فایل به آن عبور می‌دهیم.)

عمومی‌ترین پیشنهاد استفاده از آرایه Bash و حلقه‌ای برای پردازش در قطعات بزرگ است:

  •  # Bash
     files=(/usr/include/*.h /usr/include/sys/*.h)
     for ((i=0; i<${#files[*]}; i+=100)); do
       grep foo "${files[@]:i:100}" /dev/null
     done

اینحا، ما پردازش یکصد عنصر در هر نوبت را انتخاب کرده‌ایم، البته این اختیاری است، و شما می‌توانید به نسبت پیش بینی شده برای اندازه هر عنصر، در مقایسه با مقدار ARG_MAX برنامه getconf‎ سیستم مقصد، به مقدار بیشتر یا کمتر تنظیم کنید. اگر می‌خواهید تصوری به دست آورید، می‌توانیدبا استفاده از ARG_MAX و اندازه بزرگترین عنصر محاسبه کنید، اما بازهم باید «ضرایب جبرانی» برای اندازه محیط و غیره را در نظر بگیرید. آسان‌تر است فقط یک مقدار محافظه‌کارانه را به امید آنکه بهترین مقدار باشد انتخاب نمود.


CategoryShell

پرسش و پاسخ 95 (آخرین ویرایش ‎2010-01-06 14:23:30‎ توسط GreyCat)