آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

تست‌های Bash

ادامه یادداشت قبل


4. بررسی‌های Bash

فرمان test که به عنوان ‎[‎ نیز شناخته شده، یک برنامه کاربردی است که به طور معمول جایی در ‎ /usr/bin‎ یا ‎/bin‎ استقرار می‌یابد و خیلی زیاد توسط برنامه‌نویس پوسته برای اجرای آزمایش‌های معینی با متغیرها و فایلها، به کار می‌رود. در تعدادی از پوسته‌ها، از جمله Bash, دستور test به صورت دستور داخلی پوسته نیز پیاده‌سازی گردیده است.

این مورد می‌تواند نتایج شگف‌انگیزی فراهم نماید، به ویژه برای آنان که شروع به اسکریپت‌نویسی پوسته می‌نمایند و تصور می‌کنند ‎ [ ]‎ بخشی از دستور زبان پوسته است.

اگر از پوسته sh استفاده می‌کنید، انتخاب کمی دارید و استفاده از test تنها راه انجام اکثر بررسی‌هایتان می‌باشد.

گرچه اگر از Bash در اسکریپت‌نویسی استفاده می‌کنید(و من فرض می‌کنم چنین است، چون در حال خواندن این راهنما هستید)، پس می‌توانید از کلید واژه ‎[[‎ نیز استفاده کنید. هر چند بازهم از خیلی جهات همچون یک فرمان رفتار می‌کند، چندین مزیت نیز نسبت به فرمان سنتی test ارائه می‌کند.

اجازه بدهید تشریح کنم که چگونه ‎ [[‎ می‌تواند با فرمان test تعویض شود، وچطور می‌تواند به شما کمک کند از برخی اشتباهات متداول در کاربرد test پرهیز نمایید:

    $ var=''
    $ [ $var = '' ] && echo True
    -bash: [: =: unary operator expected
    $ [ "$var" = '' ] && echo True
    True
    $ [[ $var = '' ]] && echo True
    True

قسمت ‎[ $var = '' ]‎ به ‎[ = '' ]‎ بسط داده می‌شود. اولین کاری که دستور test انجام می‌دهد، شمارش شناسه‌هایش می‌باشد. چون ‎[‎ را به کار برده‌ایم، باید شناسه الزامی ] در انتها را کنار بگذاریم. در مثال اول، test دو شناسه می‌بیند: = و ''. حالا می‌داند که دو شناسه دارد، اولی باید unary operator (یک عملگر که یک عملوند می‌گیرد). اما = عملگر یگانی(unary operator) نیست(یک عملگر binary است که دو عملوند نیاز دارد)، بنابراین، test نمی‌تواند کار کند.

بله، test متغیر تهی ‎$var‎ را نمی‌بیند، زیرا BASH قبل از اینکه test حتی بتواند آن را ببیند، به هیچ بسطش داده است. نتیجه اخلاقی؟ استفاده بیشتر از نقل‌قول‌ها! کاربرد نقل‌قول‌ها در قسمت، ‎[ "$var= '' ]‎ موجب بسط آن به ‎ [ "" = '' ]‎ می‌گردد و test مشکلی ندارد.

حال آنکه، ‎[[‎ می‌تواند تمام دستور را قبل از اینکه بسط داده شود، ببیند. می‌تواند ‎$var‎ را ببیند، و نه بسط ‎$var‎ را. در نتیجه، نیازی به نقل‌قولها نمی‌باشد! ‎[[‎ مطمئن‌تر است.

    $ var=
    $ [ "$var" < a ] && echo True
    -bash: a: No such file or directory
    $ [ "$var" \< a ] && echo True
    True
    $ [[ $var < a ]] && echo True
    True

در این مثال سعی نموده‌ایم یک مقایسه رشته‌ای بین یک متغیر تهی و 'a' انجام بدهیم. شگفت‌زده می‌شویم با دیدن آنکه از اولین تلاش ما True حاصل نمی‌گردد، ولواینکه تصور می‌کردیم، می‌شود. درعوض، با خطای عجیبی که دلالت بر تلاش BASH برای باز کردن فایلی به نام ‎'a'‎ می‌نماید، مواجه می‌شویم.

ما توسط تغییر مسیر فایل گَزیده شده‌ایم. چون test دقیقاً یک برنامه کاربردی است، کاراکتر ‎<‎ در دستور ما به جای عملگر مقایسه رشته‌ای برای test،  به عنوان عملگر تغییر مسیر فایل تفسیر شده است(همانطور که باید می‌شد). BASH دستور باز کردن فایل 'a' و اتصال آن به stdin برای خواندن را دریافت نموده. برای پیش‌گیری از این مورد، لازم است, ‎ <‎ را با کاراکتر گریز پوشش دهیم، به طوری که به جای BASH برنامه test عملگر را دریافت کند. این دومین تلاش ما را تشکیل داد.

با استفاده از ‎ [[‎ می‌توانیم روی‌هم‌رفته از نابسامانی اجتناب نماییم. ‎[[‎ عملگر ‎ < را قبل از آنکه BASH آنرا برای تغییر مسیر دریافت کند، می‌بیند -- مشکل رفع می‌شود. یکبار دیگر ‎ [[‎ مطمئن‌تر است.

حتی خطرناک‌تر، استفاده از عملگر ‎ >‎ به جای عملگر ‎ <‎ مثال قبلی است. چون ‎ >‎ ماشهٔ تغییر مسیر خروجی را می‌کشد، فایلی به نام'a' ایجاد خواهد نمود. در نتیجه، هیچ پیغام خطای هشداردهنده‌ای برای ما صادر نمی‌شود که بدانیم مرتکب اشتباه شده‌ایم! به جای آن، فقط اسکریپت ما خراب می‌شود. حتی وخیم‌تر، شاید فایل مهمی را رونویسی کنیم! برای ما حدس زدن آنکه مشکل کجاست، سخت است:

    $ var=a
    $ [ "$var" > b ] && echo True || echo False
    True
    $ [[ "$var" > b ]] && echo True || echo False
    False

دو نتیجه متفاوت، شگرف. به من اعتماد کنید، وقتی می‌گویم، همیشه می‌توانید به ‎ [[‎ بیشتر از ‎ [‎ اطمینان کنید. ‎[ "$var> b ]‎ به ‎[ "a" ]‎ بسط یافته و خروجی به یک فایل جدید به نام 'b' تغییر مسیر داده می‌شود. چون ‎[ "a" ]‎ در واقع همان ‎[ -n "a" ]‎ می‌باشد و اساساً بررسی می‌شود که آیا رشته "a" غیرتهی است، نتیجه بررسی موفق است و echo True اجرا می‌شود.

با کاربرد ‎[[‎ انتظار ما که مقایسه "a" در برابر "b" است، برآورده می‌شود، و نظر به اینکه همه می‌دانیم "a" قبل از "b" مرتب می‌شود، ماشه اجرای دستور echo False کشیده می‌شود. و این چگونگی آنست که اسکریپت شما بدون پی‌بردن شما می‌تواند ناموفق بشود. هر چند که، شما یک فایل شبهه برانگیزی به نام 'b' نیز در دایرکتوری جاری خواهید داشت.

بنابراین به من باور داشته باشید، وقتی می‌گویم، ‎[[‎ مطمئن‌تر از ‎[‎ است. زیرا هر کسی به ناچار خطاهای برنامه‌نویسی را ایجاد می‌کند. افراد به طور معمول قصد ندارند، باگهایی در کدهایشان ارائه کنند. اما اتفاق می‌افتد. بنابراین مدعی نشوید که از ‎ [‎ استفاده می‌کنید و "مراقب خواهید بود که چنین اشتباهاتی مرتکب نگردید"، زیرا می‌توانم شما را مجاب کنم که مرتکب خواهید شد.

گذشته ازاین، ‎ [[‎ ویژگیهای زیر را علاوه بر ‎ [‎ ارائه می‌کند:

  • [[‎ می تواند مطابقت الگوی جانشین(glob) را انجام دهد:

    • [[ abc = a* ]]

  • [[‎ می‌تواند انطباق الگوی عبارت باقاعده(regex) را (از Bash نگارش 3.1 به بعد) انجام دهد:

    • [[ abb =~ ab+ ]]

تنها برتری test قابلیت حمل آن است.


ادامه دارد...

خوانایی

ادامه یادداشت قبل


3. خوانایی

تقریباً خوانایی کد شما به همان اندازه نتایج آن اهمیت دارد.

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

  • به من اعتماد کنید وقتی می‌گویم، هیچ بخشی از کد، هرگز 100% تکمیل نیست، به استثنای، برخی کدهای خیلی کوتاه و عاری از فایده .

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

   1     friends=( "Marcus The Rich" "JJ The Short" "Timid Thomas" "Michelangelo The Mobster" )
   2 
   3     # مطالب مهمی در باره دوستانم می‌گوید‎
   4     for name in "${friends[@]}"; do
   5 
   6         #(اولین دوست من (در لیست‎
   7         if [[ $name = ${friends[0]} ]]; then
   8             echo $name was my first friend.
   9 
  10         # شروع می‌شود M دوستان من که نامشان با ‎
  11         elif [[ $name = M* ]]; then
  12             echo "$name starts with an M"
  13 
  14         # دوستان کوتاه من‎
  15         elif [[ " $name " = *" Short "* ]]; then
  16             echo "$name is a shorty."
  17 
  18         # دوستانی که زحمت بخاطر سپردن آنها را نمی‌کشم‎
  19         else
  20             echo "I kind of forgot what $name is like."
  21 
  22         fi
  23     done

تا اینکه با موردی مشابه این روبرو شوید:

   1     x=(       Marcus\ The\ Rich JJ\ The\ Short
   2       Timid\ Thomas Michelangelo\ The\ Mobster)
   3     for name in "${x[@]}"
   4       do if [ "$name" = "$x" ]; then echo $name was my first friend.
   5      elif
   6        echo $name    |   \
   7       grep -qw Short
   8         then echo $name is a shorty.
   9      elif [ "x${name:0:1}" = "xM" ]
  10          then echo $name starts   with an M; else
  11     echo I kind of forgot what $name \
  12      is like.; fi; done

و بله، می‌دانم که این مثال کمی اغراق‌آمیز است، اما من بعضی کدهای معتبری را دیده‌ام که واقعاً شباهت بسیاری به مثال اخیر دارند.

برای سلامت خودتان این چند نکته را به یاد داشته باشید:

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

  • از پوشش با کاراکتر \ اجتناب کنید. استفاده زیاد از این کاراکترِ گریز، موجب حواس‌پرتی و آشفتگی ذهنی می‌گردد. حتی در مثالهای کوچک، تقلای ذهنی بیشتری برای فهمیدن ‎a\ b\ c‎ نسبت به ‎'a b c'‎ صرف می‌شود.

  • روش تفکرتان را، یادداشت کنید، قبل از اینکه آن را فراموش کنید. ممکن است دریابید، کدی که کاملاً متعارف حس می‌شود، می‌تواند موضوع "چه جهنمی فکر می‌کردم، وقتی این را نوشتم؟" یا "تصور انجام چه کاری از این داشتم؟".

  • سازگاری از ناراحتی ذهن پیشگیری می‌کند. در شیوه نام‌گذاری استوار باشید. در استفاده از حروف بزرگ سازگار باشید. در استفاده خود از ویژگی‌های پوسته پایدار باشید. در کدنویسی، برخلاف اطاق‌خواب، خوبست ساده و قابل پیش بینی باشید.


ادامه دارد...

نقل‌قول کردن

ادامه یادداشت قبل


2. نقل‌قولی کردن

تفکیک کلمه اهریمن درون BASH است که با جدیت تلاش می‌کند تازه‌واردها یا حتی کهنه سربازانی که سپر محافظ خود را زمین می‌گذارند، را غافلگیر کند.

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

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

    $ echo Push that word             away from me.
    Push that word away from me.
    $ echo "Push that word             away from me."
    Push that word             away from me.

حال، تصور نکنید که تفکیک کلمه برای فرو ریختن فاصله‌هاست. آنچه به طور واقعی در این مثال اتفاق می‌افتد، آنست که در مثال اول، هر یک از کلمات جمله ما به عنوان یک شناسه( argument) جداگانه به echo تحویل داده می‌شود. BASH جمله ما را به کلمات تجزیه می‌کند، از فضای سفید برای تعیین آن که هر شناسه از کجا شروع و به کجا ختم می‌گردد، استفاده می‌نماید. در مثال دوم BASH وادار شده تمام رشته نقل‌قولی شده را با هم نگاه دارد. به این معنا که به شناسه‌ها تفکیک نمی‌شوند و تمام رشته به عنوان یک شناسه به echo تحویل می‌شود. دستور echo همه شناسه‌هایی که به آن داده شود را با یک فاصله مابین آنها در خروجی چاپ می‌کند. اکنون باید اساس تجزیه کلمه را متوجه شده باشید.

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

    $ sentence="Push that word             away from me."
    $ echo $sentence
    Push that word away from me.
    $ echo "$sentence"
    Push that word             away from me.

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

فقط فاصله‌ها نیستند که باید محافظت شوند. تفکیک کلمه در فاصله‌ها، tabها، سطر جدید، یا هر کاراکتر دیگری که در متغیر IFS باشد، صورت می‌گیرد. در اینجا مثال دیگری هست، که به شما نشان می‌دهد، چطور سهل‌انگاری در استفاده از نقل‌قول‌ها، موجب تجزیه نامناسبی می‌شود:

    $ echo "$(ls -al)"
    total 8
    drwxr-xr-x   4 lhunath users 1 2007-06-28 13:13 "."/
    drwxr-xr-x 102 lhunath users 9 2007-06-28 13:13 ".."/
    -rw-r--r--   1 lhunath users 0 2007-06-28 13:13 "a"
    -rw-r--r--   1 lhunath users 0 2007-06-28 13:13 "b"
    -rw-r--r--   1 lhunath users 0 2007-06-28 13:13 "c"
    drwxr-xr-x   2 lhunath users 1 2007-06-28 13:13 "d"/
    drwxr-xr-x   2 lhunath users 1 2007-06-28 13:13 "e"/
    $ echo $(ls -al)
    total 8 drwxr-xr-x 4 lhunath users 1 2007-06-28 13:13 "."/
	 drwxr-xr-x 102 lhunath users 9 2007-06-28 13:13 ".."/ -rw-r--r-- 1 lhunath users 0 2007-06-28 13:13 "a" -rw-r--r-- 1 lhunath users 0 2007-06-28 13:13 "b" -rw-r--r-- 1 lhunath users 0 2007-06-28 13:13 "c" drwxr-xr-x 2 lhunath users 1 2007-06-28 13:13 "d"/ drwxr-xr-x 2 lhunath users 1 2007-06-28 13:13 "e"/

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

    $ friends="Marcus JJ Thomas Michelangelo"
    $ for friend in $friends
    > do echo "$friend is my friend!"; done
    Marcus is my friend!
    JJ is my friend!
    Thomas is my friend!
    Michelangelo is my friend!

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

    $ friends=( "Marcus The Rich" "JJ The Short" "Timid Thomas" "Michelangelo The Mobster" )
    $ for friend in "${friends[@]}"
    > do echo "$friend is my friend!"; done

توجه نمایید که در حلقه for قبلی، ما از ‎$friends‎ به شکلغیر نقل‌قولی استفاده کردیم. این کار BASH را برای جداکردن رشته friends به کلمات، مجاز می‌سازد. در مثال اخیر، بسط پارامتر${friends[@]}‎را نقل‌قولی کرده‌ایم . نقل‌قولی کردن یک آرایه با شاخص سراسری @ موجب می‌شود BASH آن آرایه را به صورت یک توالی از تمام عناصر که در آن هر یک در علامت نقل‌قول پیچیده شده، بسط بدهد.


ادامه دارد...

تکنیک‌ها



راه و روش‌ها

1. انتخاب پوسته شما

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

BASH ممکن است برای یادگیری و نوشتن در آن آسان باشد، اما همیشه مناسب انجام کار نیست.

در مجموعه ابزارهای اساسی، تعداد بسیاری ابزار موجود است که می‌تواند به شما کمک کند. اگر شما فقط به AWK نیاز داشته باشید، نباید یک اسکریپت شل ایجاد کنید که آن را فراخوانی کند. فقط یک اسکریپت AWK ایجاد کنید. اگر به بازیابی داده از یک فایل HTMLیا XML به یک روش معتبر نیاز دارید، نیز Bash ابزار اشتباهی برای انجام آن کار است. باید به جای آن XPath/XSLT را به کار ببرید، یا یک زبانی که کتابخانه معتبری برای تجزیه XML یا HTML دارد.

اگر تصمیم گرفتید که اسکریپت پوسته آن ابزاری است که شما می‌خواهید، اول این سؤال‌ها را از خود بپرسید:

  • در یک آینده قابل پیش‌بینی، آیا ممکن است اسکریپت شما در محیطی که Bash به طور پیش‌فرض در دسترس نیست، مورد احتیاج باشد؟

    • اگر اینطور است، پس به جای آن sh را در نظر بگیرید. sh یک شل POSIX است و ویژگی‌هایش در هر پوسته موافق با استاندارد POSIX، در دسترس می‌باشد. به این واقعیت تکیه کنید که هر سیستم POSIX قادر به اجرای اسکریپت شما خواهد بود. شما باید توازنی بین لزوم قابلیت حمل و عدم استفاده از قابلیت‌های ویژه Bash برقرار کنید.

    • به خاطر داشته باشید که این راهنما شامل sh نمی‌شود! صفحه bashism پیشنهادهایی دارد، اما کامل نیست.

  • آیا اطمینان دارید که در تمام محیط‌هایی که اسکریپت را اجرا می‌کنید یا ممکن است در آینده بخواهید اجرا کنید، Bash 3.x (یا ‎ 4.x‎) در دسترس شما خواهد بود؟
    • اگر نه، باید خود را فقط به ویژگی‌های Bash 2.x محدود نمایید.

اگر سؤلات فوق انتخاب شما را محدود نمی‌کند، از تمام ویژگی‌های Bash که لازم دارید، استفاده کنید، توجه کنید که کدام نگارش Bash برای اجرای اسکریپت شما لازم است.

استفاده از Bash نگارش 3 یا بالاتر به معنای آنست که می‌توانید از شیوه‌های اسکریپت‌نویسی کهنه و قدیمی که به دلایل بسیار خوبی با موارد خیلی بهتری جایگزین شده‌اند، اجتناب نمایید.

  • از اسکریپت‌های نمونه‌ای که در Web می‌بینید، بدون درک کامل عملکرد آنها دوری کنید. اسکریپت‌هایی که در Web پیدا می‌کنید اکثراً به نوعی ناقص هستند. از آنها کپی paste نکنید.
  • همواره از شبانگ صحیح استفاده کنید. اگر در حال نوشتن اسکریپت هستید، قرار دادن ‎#!/usr/bin/env bash‎ در بالای اسکریپت لازم است. از قلم انداختن این سرآیند یا استفاده از سرآیند ‎ #!/bin/shاشتباه است. در حالت اخیر، شما دیگر قادر به استفاده از ویژگیهای Bash نیستید. محدود به اسکریپت‌نویسی استاندارد POSIX می‌شوید(حتی اگر ‎/bin/sh شما یک پیوند به Bash باشد).

  • موقع نوشتن اسکریپت‌های پوسته، از دستور ‎[‎ استفاده نکنید. Bash جایگزین بسیار بهتری دارد: ‎[[‎. کلمه‌کلیدی ‎[[‎ در Bash از بسیاری جهات قابل اعتمادتر است و به هیچ وجه مزیتی برای چسبیدن به نوع عتیقه آن وجود ندارد. همچنین مقدار زیادی از ویژگی‌های فراهم شده برای ‎ [[‎ که در ‎ [‎ وجود ندارد( مانند انطباق الگو) را از دست می‌دهید.

  • وقت آنست که `...` نیز به فراموشی سپرده شود. این مورد با ساختار بسط سازگار نیست. به جای آن از ‎$(...)‎ استفاده کنید.

  • و به واسطه قدرت شگرف، "کاربرد بیشتر نقل‌قولها!" رشته‌ها و بسط پارامترهای خود را از تفکیک کلمات محافظت کنید. اگر به طور صحیح نقل‌قولی نکنید، تفکیک کلمات نوزادان شما را می‌خورد.

  • به جای استفاده از sed یا cut برای کار با رشته‌های ساده در Bash، استفاده از بسط‌ پارامترها را بیاموزید. اگر می‌خواهید پسوند نام فایل را حذف کنید، به جای ‎`echo "$filename" | sed 's/ \.[^.]*$//' `‎ یا بعضی دایناسورهای دیگر، از ‎ ${filename%.*} استفاده کنید.

  • به جای استفاده از expr برای انجام محاسبات ساده، از حساب داخلی استفاده کنید، مخصوصاً وقتی‌که فقط مقدار متغیری افزایش می‌یابد. اگر اسکریپتی می‌خوانید که ‎x=`expr $x + 1`‎ را به کار برده، این چیزی نیست که شما از آن تقلید کنید.

ادامه دارد...

کنترل Job



کنترل Job

اگرچه در اسکریپت‌ها معمول نیست، اما کنترلjob در پوسته‌های محاوره‌ای بسیار با اهمیت است. کنترل Job شما را قادر می‌سازد با کارهای در حال اجرای پس‌زمینه، ارتباط متقابل داشته باشید، و jobهای در حال اجرا را معوق کنید، وغیره.

اصول نظری

در سیستم‌های job، Posixها به صورت «گروههای پردازش»، با یک پردازش سرگروه، پیاده‌سازی شده‌اند. هر tty (ترمینال) یک «گروه پردازش» منفرد در پیش‌زمینه دارد، که ارتباط محاوره‌ای با ترمینال را ممکن می‌سازد. تمام گروههای پردازشی کنترلی دیگرِ همان tty به صورت jobهای پس‌زمینه می‌باشند، و می‌توانند در حال اجرا یا تعلیق باشند.

یک job موقعی تعلیق می‌شود که پردازش سر گروه آن یکی از سیگنال‌های SIGSTOP یا SIGTSTP یا SIGTTIN یا SIGTTOU را دریافت کند. سیگنالهای SIGTTIN و SIGTTOU هر وقت که یک job پس‌زمینه سعی کند از ترمینال بخواند یا در آن بنویسد، به طور خودکار ارسال می‌شوند--- این است چرای آنکه ‎cat &‎ به جای آنکه در پس‌زمینه اجرا گردد، فوراً به حالت تعلیق می‌رود.

فشردن برخی کلیدها در ترمینال باعث ارسال سیگنالها به تمام پردازشها در گروه پردازشی پیش‌زمینه می‌گردند. این کلیدها می‌توانند با فرمان stty پیکربندی شوند، اما معمولاً به طور پیش‌فرض تنظیم گردیده‌اند:

  • Ctrl-Z سیگنال SIGTSTP را به job پیش‌زمینه ارسال می‌کند(به طور معمول آنرا به تعلیق در می‌آورد)

  • Ctrl-C سیگنال SIGINT را به job پیش‌زمینه ارسال می‌کند( به طور معمول آن را خاتمه می‌دهد)

  • ‎Ctrl-\‎ سیگنال SIGQUIT را به job پیش‌زمینه ارسال می‌کند(به طور معمول موجب ایجاد یک نسخه فایل core و سپس انصراف از job می‌گردد)

شیوه عمل

کنترل Job به طور پیش‌فرض در پوسته‌های محاوره‌ای عمل می‌کنند. امکان آن هست که برای اسکریپت‌ها با دستور set -m یا set -o monitor فعال گردد.

پردازش job پیش‌زمینه با فشردن ‎ Ctrl-Z‎ می تواند به حالت تعلیق درآید. در bash هیچ راهی برای ارجاع به job پیش‌زمینه وجود ندارد: اگر job پیش‌زمینه‌ای غیر از bash وجود داشته باشد، bash در انتظار می‌ماند تا آن job خاتمه یابد، و ازاینرو نمی‌تواند هیچ کدی را اجرا نماید( حتی trapها تا خاتمه یافتن jobپیش‌زمینه به تعویق می‌افتند). بنابراین، دستورات زیر فقط برای jobهای پس‌زمینه( و تعلیق شده ) کار می‌کنند.

کنترل Job فرمانهای زیر را فعال می‌کند:

  • ‏[مشخصه job] ‏fg: یک job پس‌زمینه را به پیش‌زمینه می‌آورد.

  • ‏[... مشخصه job ] ‏bg : یک job به تعلیق درآمده را در پس‌زمینه اجرا می‌کند.

  • suspend:پوسته را به حالت تعلیق می‌برد( اکثراً برای موقعی مناسب است که پردازش والد، یک پوستهِ با کنترل job باشد).

دستورات دیگر برای محاوره با jobها عبارتند از:

  • jobs [options] [jobspec ...]‎ : این jobهای تعلیقی و پس‌زمینه را لیست می‌کند. گزینه‌ها شامل ‎-p‎(فقط شماره شناسایی پردازش را لیست می‌کند)،‎ -s‎ (فقط jobهای تعلیقی را لیست می‌کند)، و ‎-r‎ (فقط jobهای در حال اجرا در پس‌زمینه را لیست می‌کند). اگر یک یا چند مشخصه( jobspec) معین شده باشد، تمام jobهای دیگر صرفنظر می‌شوند.

  • kill می‌تواند به جای شماره شناسایی، یک مشخصه job قبول کند.

  • disown به bash می‌گوید یک job موجود را فراموش کند. این دستور bash را از ارسال خودکار سیگنال SIGHUP به پردازشهای آن job مانع می‌شود، اما همچنین به معنای آنست که دیگر نمی‌توان با jobspec به آن رجوع نمود.

بنابراین، تمام اینها به چه معناست؟ کنترل Job به شما اجازه می‌دهد در یک نشست منفرد ترمینال، امور چندگانه‌ای در حال اجرا داشته باشید. (در روزگار گذشته، زمانی که شما فقط یک ترمینال روی میز داشتید، و روشی برای ایجاد ترمینالهای مجازی جهت اضافه کردن آن نبود)، بسیار زیاد اهمیت داشت. در سیستم‌ها و سخت افزار مدرن، شما انتخاب‌های بیشتری در دسترس دارید --برای مثال می‌توانید screen یا tmux را برای به دست آوردن ترمینالهای مجازی اجرا نمایید. یا داخل یک نشست X، می‌توانید xterm یا شبیه‌سازهای ترمینال بیشتری باز کنید(و می‌توانید این دو را به خوبی باهم ترکیب کنید).

اما گاهی اوقات، یک کنترل job ساده(تعلیق یا پس‌زمینه) سودمند واقع می‌شود. شاید یک پشتیبان‌گیری را شروع کرده باشید و بیش از آنچه انتظار دارید طول بکشد. می‌توانید با Ctrl-Z آن را به تعلیق درآورید و سپس آن را با دستور bg در پس‌زمینه قرار بدهید، واعلان فرمان پوسته را بازپس بگیرید،به طوری که در ضمن اینکه پشتیبان‌گیری در پس‌زمینه انجام می‌شود، بتوانید کار دیگری در همان نشست ترمینال انجام بدهید.

مشخصات Job

مشخصه job یا "jobspec" روشی برای ارجاع به پردازشی است که job را می‌سازد. یک مشخصه می‌تواند چنین باشد:

  • %n ‎ جهت ارجاع به job شماره n.

  • %str ‎ برای ارجاع به آن job که با دستوری که رشته str در ابتدای آنست، شروع شده باشد. اگر بیش از یک job اینگونه موجود باشد، یک خطا رخ می‌دهد.

  • %?str ‎ برای ارجاع به یک job که با دستوری شامل رشته str شروع شده باشد. اگر بیش از یک job از این قبیل باشد، یک خطا رخ می‌دهد.

  • %% یا ‎%+‎ برای ارجاع به job اخیر: آنکه آخرین job شروع شده در پس‌زمینه، یا تعلیق شده در پیش‌زمینه باشد. fg و bg در صورتی که jobspec داده نشده باشد، روی این job عمل می‌کنند.

  • %-‎ برای job قبلی( که قبل از job فعلی %% بود).

امکان آن هست که یک دستور اختیاری را بایک jobspec به کار برد، این ساختار: ‎jobs -x ''cmd args...''‎. در اینصورت arg‌هایی که مشخصه job به نظر می‌آیند را با شماره شناسایی‌های سرگروه پردازشهای هر job متناظر تعویض می‌کند، سپس دستور را اجرا می‌کند. برای مثال، ‎jobs -x strace -p %%‎ فرمان strace را به job اخیر پیوست خواهد نمود(اگر به جای حالت تعلیق در حال اجرا در پس‌زمینه باشد سودمندتر است).

مطلب انتهایی، یک مشخصه job عریان می‌تواند به عنوان دستور به کار رود: ‎%1‎ معادل است با ‎ fg %1‎, در حالیکه ‎%1 &‎ معادل با‎ bg %1 ‎ می‌باشد.

See Also

مدیریت پردازش تمرین‌هایی برای کار با پردازشهای چندتایی را بحث می‌کند. یک مثال از کاربرد کنترل job در داخل اسکریپت نیز دارد.