فصل 15- فرمان‌های داخلی و builtinها

‎15.1‎- فرمانهای کنترل job

برخی از فرمانهای کنترل job زیرین، یک معرف job را به عنوان شناسه دریافت می‌کنند. جدول انتهای این فصل را ملاحظه کنید.

jobs

این فرمان، jobهای در حال اجرای پس‌زمینه را با ارایه شماره job آنها، لیست می‌کند. به اندازه ps سودمند نیست.


jobها و پردازش‌ها بسیار به آسانی با یکدیگر اشتباه گرفته می‌شوند. برخی builtinها، از قبیل kill‏، disown‏، و wait یک شماره job یا یک شماره پردازش را به عنوان شناسه می‌پذیرند. فرمان‌های fg‏، bg‏، و jobs فقط یک شماره job را قبول می‌کنند.

bash$ sleep 100 &
[1] 1384

bash $ jobs
[1]+  Running                 sleep 100 &

«1» شماره ‎job‎ است (jobها توسط پوسته جاری نگهداری می‌شوند). «1384»‏ شماره ‎ID‎ پردازش یا ‎PID‎ است (پردازش‌ها توسط سیستم نگهداری می‌شوند). برای کشتن این job/پردازش، یا یک ‎kill %1‎ یا یک kill 1384 عمل می‌کند.

باتشکر از ‎S.C.‎


disown

job(ها) را از جدول jobهای فعال پوسته حذف می‌کند.

fg‏، bg

فرمان fg یک job در حال اجرا در پس‌زمینه را به پیش‌زمینه تغییر وضعیت می‌دهد. فرمان bg یک job تعلیق شده را دوباره شروع کرده و آن را در پس‌زمینه اجرا می‌کند. در صورتیکه شماره ‎job‎ تعیین نشده باشد، آنوقت فرمان‌های fg یا bg بر روی job در حال اجرای جاری عمل می‌کنند.

wait

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

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

مثال ‎15-26‎. انتظار برای پایان یافتن یک پردازش، قبل از پیشروی

#!/bin/bash

ROOT_UID=0   # فقط کاربرانِ با ‎$UID‎ برابر ‎0‎ دارای مزایای ‎root‎ هستند.
E_NOTROOT=65
E_NOPARAMS=66

if [ "$UID" -ne "$ROOT_UID" ]
then
  echo "Must be root to run this script."
  #  «بچه برو پی کارت، از موقع خوابت گذشته است.»
  exit $E_NOTROOT
fi  

if [ -z "$1" ]
then
  echo "Usage: `basename $0` find-string"
  exit $E_NOPARAMS
fi


echo "Updating 'locate' database..."
echo "This may take a while."
updatedb /usr &                     #       باید به عنوان root اجرا شود.

wait
#               تا وقتی که ‎updatedb‎ پایان یابد، بقیه اسکریپت اجرا نشود.
#     قبل از جستجوی نام فایل، بانک اطلاعات به روزرسانی شده را لازم دارید.

locate $1

#    بدون فرمان ‎wait‎، در یک وضعیت وخیم‌تر، در حالیکه ‎updatedb‎ هنوز در حال 
#+.اجرا بود، اسکریپت با رها کردن آن به صورت یک پردازش بی پدر، خارج می‌شد


exit 0

wait می‌تواند به طور اختیاری یک معرف job را به عنوان یک شناسه دریافت کند، به عنوان مثال، wait %1‎ یا wait ‎$PID‎‎‎[1]‎‏. جدول ‎job id‎ را ملاحظه کنید.

tip

در درون یک اسکریپت، اجرای یک فرمان در پس‌زمینه به وسیله یک کاراکتر ‎ ampersand (&)‎ ممکن است موجب شود که اسکریپت تا زدن کلید ENTER هنگ شود. به نظر می‌رسد این مورد با فرمانهایی اتفاق می‌افتد که در stdout می‌نویسند. این می‌تواند یک دردسر مهم باشد.

#!/bin/bash
# test.sh		  

ls -l &
echo "Done."
bash$ ./test.sh
Done.
[bozo@localhost test-scripts]$ total 1
-rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
_
 

به طوری که ‎Walter Brameld IV‎ آن را تشریح می‌کند:

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

‎1‎. اسکریپت فرمان را در پس‌زمینه راه‌اندازی می‌کند.
‎2‎. اسکریپت خارج می‌شود.
‎3‎. پوسته اعلان را صادر می‌کند. (مترجم: اما در نمایشگر دیده نمی‌شود چون stdout در اختیار فرمان پس‌زمینه است)
‎4‎. فرمان پس‌زمینه اجرا و نوشتن متن در کنسول را ادامه می‌دهد.
‎5‎. فرمان پس‌زمینه خاتمه می‌یابد.
‎6‎. کاربر در انتهای خروجی اعلانی نمی‌بیند، فکر می‌کند اسکریپت در حال هنگ است.

ظاهراً چاره این مشکل، قرار دادن یک فرمان wait بعد از فرمان پس‌زمینه است.

#!/bin/bash
# test.sh

ls -l &
echo "Done."
wait
bash$ ./test.sh
Done.
[bozo@localhost test-scripts]$ total 1
-rwxr-xr-x    1 bozo     bozo           34 Oct 11 15:09 test.sh
bash$ 
تغییر مسیر خروجی فرمان به یک فایل یا حتی به ‎/dev/null‎ نیز این مشکل را برطرف می‌کند.


suspend

این فرمان دارای اثری مشابه ‎Control-Z‎ است، اما پوسته را به حالت تعلیق در می‌آورد (پردازش والد پوسته باید در یک زمان مناسب آن را ادامه بدهد)‏.

logout

خروج یک پوسته ‎login‎، به طور اختیاری با مشخص کردن یک وضعیت خروج.

times

آماری در باره زمان صرف شده سیستم برای اجرای فرمان‌ها، در قالب زیر ارایه می‌کند:

0m0.020s 0m0.020s

این توانایی ارزش نسبتاً محدودی دارد، چون برای شناسایی و سنجش کارایی اسکریپت‌های پوسته رایج نیست.

kill

خاتمه دادن اجباری یک پردازش به وسیله فرستادن یک سیگنال فسخ متناسب ( مثال ‎17-6‎ را ببینید).

مثال ‎15-27‎. اسکریپتی که خودش را می‌کُشد

#!/bin/bash
# self-destruct.sh

kill $$   #  .در اینجا اسکریپت پردازش خودش را از بین می‌برد
          # به خاطر بیاورید که ‎$$‎ برابر با ‎PID‎ اسکریپت است.

echo "This line will not echo."
# .در عوض، پوسته یک پیغام «فسخ» به خروجی استاندارد می‌فرستد

exit 0                         #       خروج عادی است؟ خیر!

#           بعد از اینکه این اسکریپت به طور زودهنگام خاتمه
#+                 می‌یابد، کدام وضعیت خروج را برگشت می‌دهد؟
#
 bash$ sh self-destruct.sh
 bash$ echo $?
 143
# 143 = 128 + 15 # ^ SIGTERM سیگنال


‎kill -l‎ تمام سیگنال‌ها را لیست می‌کند (همانطور که فایل ‎/usr/include/asm/signal.h‎ لیست می‌کند). یک ‎kill -9‎ یک kill حتمی است، که معمولاً به پردازشی که سرسختانه از کُشته شدن با یک ‎kill ساده خودداری می‌کند، خاتمه می‌دهد. گاهی اوقات، ‎kill -15 نیز عمل می‌کند. یک پردازش zombie، یعنی یک پردازش فرزند که خاتمه یافته، اما پردازش والدش هنوز آن را kill نکرده است، نمی‌تواند به وسیله یک کاربر login کرده kill بشود -- شما نمی‌توانید چیزی را قبلاً مُرده از بین ببرید -- اما init دیر یا زود آن را تصفیه خواهد نمود.

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

killall

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


این دستور به فرمان killall در ‎/usr/bin‎ ارجاع می‌کند، نه به اسکریپت killall در ‎/etc/rc.d/init.d‎


command

دستور command مستعارها و توابع را برای فرمانی که بلافاصله به دنبال آن می‌آید، از کار می‌اندازد.

bash$ command ls


این یکی از سه دستور پوسته است که بر پردازش فرمان اسکریپت اثر می‌گذارد. دو فرمان دیگر builtin و enable هستند.


builtin

فراخوانی ‎builtin ‎BUILTIN_COMMAND‎ فرمان ‎BUILTIN_COMMAND‎ را به عنوان یک builtin پوسته اجرا می‌کند، به طور موقت فرمان‌های خارجی سیستم و توابع با همان نام را از کار می‌اندازد.

enable

این یک فرمان builtin پوسته را فعال یا غیرفعال می‌کند. به عنوان یک مثال، enable -n kill‎ فرمان builtin پوسته با نام kill را غیرفعال می‌کند، به طوریکه وقتی Bash پس از آن با kill روبرو شود، فرمان خارجی ‎/bin/kill‎ را احضار می‌کند. مترجم: برای فعال کردن دستور غیرفعال شده با گزینه ‎-n‎ نام آن دستور را بدون گزینه به enable بدهید.

گزینه ‎-a‎ با enable تمام builtinهای پوسته را با نشان دادن اینکه آیا فعال هستند یا خیر، لیست می‌کند. گزینه ‎-f filename‎ اجازه می‌دهد که enable یک builtin را از یک فایل object کامپایل شده، به صورت یک ماژول کتابخانه‌ای به اشتراک گذاشته شده ‎(DLL)‎ بارگیری نماید ‎[2]‎.

autoload

این یک تبدیل Bash از autoloader پوسته ksh است. با کاربرد مناسب autoload، تابعی با یک اعلان autoload، در اولین احضارش، از یک فایل خارجی بارگیری خواهد گردید. ‎[3]‎ این کار منابع سیستم را صرفه‌جویی می‌کند.

توجه نمایید که autoload بخشی از هسته مرکزی برپاسازی Bash نیست. لازم است به وسیله enable ‎-f‎ در آن بارگذاری بشود (بالا را ببینید)‏.

جدول ‎15-1‎. ‏(تعیین هویت کننده)معرف‌های Job

نمادمعنی
‎‎%N‎‎ شماره Job‏ ‎[N]‎‎
‎‎%S‎‎ فراخوانی (در خط فرمان) job شروع شوند با رشته S
‎‎%?S‎‎ فراخوانی (از خط فرمان) job شامل رشته S در داخل نام آن
%% job «جاری» (آخرین job متوقف شده در پیش‌زمینه یا شروع شده در پس‌زمینه)
‎‎%+‎‎ job «جاری» (آخرین job متوقف شده در پیش‌زمینه یا شروع شده در پس‌زمینه)
‎‎%-‎‎ آخرین job
‎‎$!‎‎ آخرین پردازش پس‌زمینه

یادداشت‌ها

[1]

البته، این فقط برای پردازش‌های فرزند صادق است.

[2]

به طور نمونه، کد منبع C تعدادی از builtinهای قابل بارگذاری، در دایرکتوری ‎/usr/share/doc/bash-?.??/functions‎ قرار دارد.

توجه کنید که گزینه ‎-f‎ با enable قابل حمل به تمام سیستم‌ها نیست.

[3]

همان نتیجه autoload می‌تواند به وسیله ‎typeset -fu‎ حاصل گردد.