۱۳۸۹ دی ۸, چهارشنبه

ترفند 9 - خاموش کردن خودکار کامپیوتر بعد از تمام شدن کار یک برنامه

تا حالا شده که بخواین وقتی اجرای فلان برنامه تمام شد کامپیوتر خودبخود خاموش بشه؟ مثلا وقتی که برنامه‌ی محاسبه‌ی عدد پی که نوشته‌اید خاتمه پیدا کرد، یا نصب برنامه‌ای تمام شد، یا وقتی که فرمت کردن هارد دیسک خاتمه یافت یا ...
در این ترفند، یاد می‌گیرید که چطور این کار را خودتون و بدون ابزارهای اضافی انجام بدین: به کمک خط فرمان (command-prompt) ویندوز، یا ترمینال (terminal) لینوکس.
اول روش ویندوزی رو بگم، بعد بریم سراغ لینوکس.

راه ساده، اگر می‌دانستم ...

اول راه ساده‌اش رو بگم: اگر از قبل بدونید که فلان برنامه رو می‌خواین اجرا کنین که طول می‌کشه، و لازمه که کامپیوتر بعد از اجرای برنامه خودبخود خاموش بشه، روش انجام کار خیلی ساده است. کافیه یک اسکریپت دو خطی بنویسید. (توی ویندوز به بچ فایل یا batch file معروفه)
start /wait zamanbar
shutdown -s -f
که zamanbar در واقع مسیر و نام برنامه‌ایست که می‌خواهید اجرا شود و بعد از خاتمه‌اش کامپیوتر خاموش شود. این دو خط را در یک فایل با پسوند bat ذخیره می‌کنید و اجراش می‌کنید. والسلام!

اما اگر برنامه zamanbar رو اجرا کرده‌اید و بعد از مثلا یک ساعت، تازه متوجه شدید حالا حالاها اجرای برنامه طول می‌کشه و شما مثلا جایی قرار دارید و باید برید، آن وقت چطور؟ آن وقت چه کار می‌کنید که پس از خاتمه اجرای برنامه، کامپیوتر خودش خاموش بشه؟

در ویندوز

خط فرمان (cmd یا همان Commandline Prompt) را باز کنید.
شکلی کلی دستوری که باید اجرا کنید اینه:
for /L %i in (1,0,2) do @(
  ping 127.0.0.1 -n 2 >nul & (
    wmic process where name="zamanbar.exe" list brief | findstr Name
  ) || shutdown -s -f
)
فقط باید عبارت zamanbar.exe را با نام اجرایی برنامه خودتان عوض کنید.
توجه کنید که برای اجرای این کد باید سطح دسترسی Administrator داشته باشید. بنابراین هم باید از یک user با این سطح دسترسی استفاده کنید و هم در ویندوزهای ویستا به بعد، گزینه Run as administrator را موقع باز کردن cmd زده باشید.
هشدار:
قبل از اجرای دستور بالا یکبار دستور
wmic process where name="zamanbar.exe" list brief
را اجرا کنید تا مطمئن شوید نام اجرایی برنامه را درست نوشته‌اید. وگرنه به محض اجرای دستور، عملیات خاموش شدن کامپیوتر آغاز می‌شود و باید بدنبال خر و باقالی‌ها بدوید!
البته خیلی هم سخت نیست: 30 ثانیه فرصت دارید تا بنویسید:
shutdown -a
و عملیات خاموش شدن را لغو کنید. سوییچ a به معنی abort است، خاطرتان باشد!
سوال: چطوری کار می‌کنه؟
چیزی که دیدید برنامه‌ایست که به زبان خط فرمان ویندوز نوشته شده است. هر بخش این برنامه یه کار بخصوص انجام می‌ده:
  • for /L %i in (1,0,2) do @(...): در واقع یک حلقه بی‌پایان است. کدی که به جای ... در این دستور قرار بگیره مرتبا و پشت سرهم اجرا خواهد شد.
    در واقع for /L باعث می‌شه که متغیر %i از عدد 1 آغاز کنه و تا عدد 2 بشماره. منتها این شمارش با گام‌های 0تایی انجام می‌شه. یعنی کامپیوتر به جای اینکه بشماره 1، 2، 3 و هر بار یک گام به جلو بره، مرتب درجا می‌زنه: 1، 1، 1، و به همین ترتیب هرگز به 2 نمی‌رسه: یک حلقه بی‌پایان!
  • ping 127.0.0.1 -n 2 >nul: این دستور اجرای برنامه را یک ثانیه معطل می‌کنه. یعنی یک ثانیه صبر کن و بعد ادامه بده.
    اگر این دستور رو نگذاریم، حلقه بی‌پایانی که نوشتیم مرتبا اجرا می‌شه. نتیجه اینه که سی‌پی‌یو به سقف می‌چسبه و سرعت کامپیوتر پایین میاد. ما نمی‌خواهیم اینطور بشه، پس هر بار به کامپیوتر یک ثانیه استراحت می‌دیم.
    ظاهر دستور یه مقدار عجیبه، چون ما از دستور ping استفاده می‌کنیم تا کامپیوتر دوبار خودش رو پینگ (ping) کنه. (شبکه‌ای‌ها می‌دونن پینگ چیه!) چون بین هر دو پینگ یک ثانیه فاصله وجود داره، اجرای دستور یک ثانیه تاخیر ایجاد می‌کنه. عجیبه، ولی توی ویندوز یک ترفند شناخته شده است!
  • wmic process where name="zamanbar.exe" list brief | findstr Name: اما بخش جالب ماجرا! این در واقع دو دستوره که با عملگر لوله‌کشی | به هم وصل شده‌اند و کارشان تست این است که برنامه‌ی zamanbar.exe در حال اجرا هست یا خیر.
    دستور wmic یک دستور همه‌کاره در ویندوزه که جای بحثش نیست. ما در اینجا ازش سوال می‌کنیم چه پروسه‌ها (برنامه‌ها)یی با نام zamanbar.exe در حال اجرا هستند. جواب یا مشخصات اون برنامه است یا عبارت No Instance(s) Available.
    بعد برای اینکه بفهمیم جواب چی بوده، از دستور findstr استفاده کردیم. کار findstr این است که یک رشته (در واقع یک عبارت منظم یا regular expression) بگیرد و در ورودی خودش به دنبال آن بگردد. اما ورودی این دستور با عملگر لوله‌کشی به خروجی دستور قبلی وصل شده، یعنی هر چه wmic بنویسه به دستور findstr داده می‌شود. بنابراین به کمک عملگر لوله‌کشی، این دستور در خروجی دستور قبلی به دنبال کلمه Name می‌گرده، اگر بود یعنی پروسه در حال اجرا بوده، و اگر این کلمه وجود نداشت یعنی پروسه‌ی zamanbar.exe کارش تمام شده. (هشدار: در حالت عادی findstr به بزرگی و کوچکی حرف حساس است. پس Name را دقیقا همینطور بنویسید!)
  • shutdown -s -f: اینهم که مشخصه: به محض اجرای این دستور، ویندوز عملیات خاموش شدن (Shutdown) رو آغاز می‌کنه. سوییچ -s یعنی ما دلمون می‌خواد کامپیوتر خاموش بشه. (با این دستور می‌شه کامپیوتر رو ریستارت هم کرد) سوییچ -f هم که یعنی با زور (force) برو جلو: اگر برنامه‌ای خواست جلوی خاموش شدن رو بگیره، به حرفش گوش نکن!
    اما چه چیزی باعث می‌شه که این دستور بی‌موقع اجرا نشه؟ یعنی چرا تا زمانیکه zamanbar.exe در حال اجراست، این دستور اجرا نمی‌شه؟؟
    جواب در عملگر || نهفته است. دو دستوری که دو طرف این عملگر نوشته می‌شوند با هم رابطه پیدا می‌کنند: اگر بخش سمت چپ تونست کارش را با موفقیت انجام بده، بخش سمت راست دیگه اجرا نمی‌شه. اما اگر بخش سمت چپ شکست خورد (که در اینجا بخش چپ، همان عبارت داخل پرانتز است، یعنی دو دستور wmic و findstr که شکست خوردنشان یعنی برنامه‌ای با نام zamanbar.exe در حال اجرا نبوده) آن‌وقت، برنامه سمت راست (یعنی shutdown) اجرا خواهد شد.

در لینوکس

بالاخره! می‌رسیم به روش لینوکسی. در اصل نوشتن کل این پست از همین قسمتش شروع شد!!
معمولا در لینوکس به خط فرمان Terminal گفته می‌شه و جاییه که همه بالاخره یکبار گذارشون بهش افتاده!
نمونه کد:
while ps -p 1234; do sleep 1; done; shutdown -h 0
فقط باید 1234 را با شماره پروسه (Process ID) برنامه زمان‌برتان عوض کنید. برای بدست آوردن این عدد می‌تونید از برنامه‌ای مانند System Monitor یا مثلا دستور ps auxw استفاده کنید.
توجه کنید که برای اجرای این کد باید سطح دسترسی root داشته باشید.
سوال: چطوری کار می‌کنه؟
  • while ???; do ... done;: این یه حلقه است. تا زمانیکه شرط ??? برقرار باشه، دستور ... مرتبا و پشت سر هم اجرا می‌شه. شرط رو به صورت‌های مختلفی می‌شه نوشت، که در اینجا ما از خروجی یک برنامه استفاده می‌کنیم. اگر اون برنامه موفقیت آمیز بود، شرط هم برقراره و اگر شکست خورد، شرط هم دیگه برقرار نخواهد بود.
  • ps -p 1234: این دستور که در واقع بخش شرطی حلقه while رو تشکیل می‌ده، کارش اینه که تست کنه ببینه برنامه zamanbar در حال اجرا هست یا خیر.
    کار دستور ps لیست کردن پروسه‌ها (Process/برنامه‌ها)ی در حال اجراست. انواع شرط رو می‌شه تعیین کرد که چه پروسه‌هایی نمایش داده بشوند یا چه خصوصیاتی از اونها لیست بشه. ما اینجا با سوییچ -p بهش گفتیم فقط پروسه با شماره 1234 را نمایش بده. بنابراین اگر این پروسه پیدا شد (در حال اجرا بود) ps موفق بوده، وگرنه شکست می‌خوره و عبارت شرطی حلقه while برقرار نخواهد بود.
  • sleep 1: بعید می‌دونم بتونید حدس بزنید کار این دستور چیه! ... کار این دستور اینه که یک ثانیه به کامپیوتر استراحت بده و هیچ کاری نکنه.
    دلیل استفاده‌اش رو هم در بخش ویندوزی گفتم: اجرای مرتب حلقه پشت سرهم، کل وقت سی‌پی‌یو رو به خودش اختصاص می‌ده و کامپیوتر رو کند می‌کنه. بهتره هر بار کمی به کامپیوتر استراحت بدیم تا سایر برنامه‌ها بتونند کارشون رو انجام بدهند.
  • shutdown -h 0: اصل کار! به محض اجرای این دستور، عملیات خاموش شدن (halt) آغاز خواهد شد. سوییچ -h یعنی ما دلمون می‌خواد کامپیوتر خاموش بشه. (مثل ویندوز، با این دستور می‌شه کامپیوتر رو ریستارت هم کرد) سوییچ 0 هم یعنی ما دلمون می‌خواد همین حالا (صفر ثانیه بعد!) این کار انجام بگیره.
    توجه کنید که این دستور خارج از حلقه while قرار گرفته. بنابراین تا زمانیکه حلقه در حال اجراست (یعنی تا زمانیکه ps پروسه zamanbar رو پیدا می‌کنه) این دستور اجرا نخواهد شد. بلکه بلافاصله پس از خاتمه حلقه نوبت اجرا بهش می‌رسه.

امیدوارم مطلب براتون مفید بوده باشه.

۴ نظر:

  1. توضیحات خیلی عالی بود!تنها جایی که نیاز به توضیح بیشتر داشت ادامه اون PING بود بعد از آدرس لوپ-بک که
    -n 2 >nul یعنی چه!
    در کل بسیار آموزنده بود! بازم از این اسکریپ تها و توضحیاتشون بنویس!

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

    پاسخحذف
  3. جالب بود ولی برای بدست آوردن PID برنامه در لینوکس راحتتر اینه که از دستور pgrep بعلاوه اسم برنامه استفاده کنیم.
    مثلا pgrep test.a
    راستی اگه برنامه چند تا fork داشته باشه، شاید یه کم کار مشکلتر بشه شاید هم مشکلی پیش نیاد
    یاعلی

    پاسخحذف
  4. درسته. موافقم.
    حتی می‌توان دستور را با همین pgrep به جای ps پیاده کرد.
    --------------------
    اما نسخه ویندوزی با نسخه لینوکسی یک تفاوت داره که در متن بهش اشاره نشده. علت این تفاوت هم در همین نکته است که در ویندوز از نام برنامه استفاده می‌کنیم ولی در لینوکس با شماره پروسه، برنامه را تشخیص می‌دهیم.
    بنابراین اگر در نسخه ویندوزی چند برنامه با یک نام در حال اجرا باشد، کامپیوتر تا زمانی که همه آنها بسته نشده خاموش نخواهد شد.
    در عوض در نسخه لینوکسی (که از شماره پروسه استفاده کرده) خاموش شدن کامپیوتر تنها منوط به بسته شدن همان پروسه است. همین مساله در مورد برنامه‌هایی که fork می‌کنند بسته به شرایط می‌تواند مطلوب کاربر باشد یا خیر.

    هر دو اسکریپت را می‌توان تغییر داد تا مانند دیگری عمل کند. که البته اشاره به این نکته و شکافتن آن؛ پست را از این هم طولانی‌تر می‌کرد. :)

    پاسخحذف