منبع اصلی نوشتار زیر در این لینک قرار دارد

timeout برای انصراف از اجرای فرمان


چگونه می‌توانم فرمانی را اجرا کنم و انصراف از آن پس از N ثانیه را داشته باشم(timeout)

نخست بررسی کنید آیا می‌شود به فرمانی که اجرا می‌کنید به طور مستقیم گفته شود، زمان مورد نظر به پایان رسید. روشهای تشریح شده در اینجا فرافکنی hack مانندی برای مجبور نمودن دستور، به خاتمه یافتن پس از سپری شدن یک زمان معین می‌باشند. پیکربندی فرمان شما به طور صحیح، همواره نسبت به جایگزین‌های ذیل ارجحیت دارد.

اگر فرمان هیچ پشتیبانی درونی از توقف پس از زمان تعیین شده نداشته باشد، آنوقت بهترین جایگزین‌ها، برخی برنامه‌های خارجی به نام timeout و doalarm می‌باشند. بعضی توزیع‌های لینوکس نگارش tct از timeout را به عنوان یک بسته ارائه می‌کنند. یک نگارش گنو از timeout نیز وجود دارد، که در انتشارهای اخیر coreutils ضمیمه گردیده است.

مراقب باشید: بعضی از پیاده‌سازی‌های timeout به طور پیش فرض، یک سیگنال SIGKILL ‏(‎kill -9‎) صادر می‌کنند، که تقریباً همان بیرون آوردن کابل برق است( یعنی از دست دادن هر شانس برنامه برای به انجام رساندن کارهایش که اغلب منجر به خراب شدن داده‌هایش می‌شود ). شما به جای آن باید از سیگنالی استفاده کنید که به برنامه اجازه بدهد خودش را خاموش کند ‎(SIGTERM)‎. برای اطلاعات بیشتر در باره SIGKILL، مدیریت پردازش را ملاحظه کنید.

تفاوت عمده بین doalarm و timeout آنست که doalarm برنامه را بعد از تنظیم مهلت مقرر به اجرا در می‌آورد، که در یک WrapperScript آن را شگفت‌آور می‌سازد، در حالیکه timeout برنامه را به عنوان یک فرزند شروع می‌کند و سپس همراه آن می‌ماند( هر دو پردازش به طور همزمان وجود دارند)، که به ما مجال می‌دهد در صورت لزوم بیش از یک سیگنال ارسال کنیم.

اگر شما یکی از برنامه‌های فوق را ندارید یا نمی‌خواهید، می‌توانید از پرل یک سطری برای تنظیم یک ALRM و سپس اجرای برنامه‌ای که می‌خواهید تحت محدودیت زمانی اجرا گردد استفاده کنید. در هر حالت، شما باید بفهمید که برنامه شما با سیگنال SIGALRM چه کاری انجام می‌دهد، برنامه‌های دارای به هنگام سازی‌های متناوب معمولاً به جای مرگ در موقعی که آن سیگنال را دریافت می‌کنند، برای این منظور از ALRM و به هنگام سازی، استفاده می‌کنند.

doalarm() { perl -e 'alarm shift; exec @ARGV' "$@"; }

doalarm ${NUMBER_OF_SECONDS_BEFORE_ALRMING} program arg arg ...

اگر شما نمی‌توانید یا نمی‌خواهید یکی از این برنامه‌ها را به کار ببرید(که به راستی در هسته اصلی ابزارهای یونیکس در 30 سال قبل از این گنجانده شده بود!)، آنوقت بهترین کاری که می‌توانید انجام بدهید، راه حل بدشکلی مانند این است:

   command & pid=$!
   { sleep 10; kill $pid; } &

به طوری که فوراً متوجه خواهید شد، اگر آن را در پوسته محاوره‌ای اجرا نمایید صرف نظر از اینکه وضعیت timeout شروع بشود یا نه، واقعاً یک بی‌نظمی ایجاد خواهد کرد. پاک کردن آن سزاوار صرف وقت من نیست. همچنین، با هیچ فرمانی مانند top که به ترمینال پیش‌زمینه نیاز داشته باشد، نمی‌تواند به کار برود.

انجام کاری مشابه آن، اما نگهداری command در پیش‌زمینه امکان‌پذیر است:

   bash -c '(sleep 10; kill $$) & exec command'

kill $$‎ پوسته را kill می‌کند، به استثنای آن exec باعث می‌شود فرمان PID پوسته را تحویل بگیرد. لازم است از ‎bash -c‎ استفاده شود برای این که پوسته فراخواننده تعویض نشود، در ‎bash 4‎، استفاده از پوسته فرعی به جای آن امکان‌پذیر است:

   ( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec command )

اسکریپت پوسته "timeout" (با فرمان timeout اشتباه نشود) از رویکرد دوم در بالا استفاده می‌کند. مزیت آن کارکرد فوری آن است(نیازی به کامپایل شدن ندارد)، اما مشکلاتی دارد، به عنوان مثال با برنامه‌هایی که از ورودی استاندارد می‌خوانند.

واقعاً، به جای آن فقط از timeout یا doalarm استفاده کنید.


پرسش و پاسخ 68 (آخرین ویرایش ‎2010-07-27 13:03:01‎ توسط GreyCat)