تقریباً خوانایی کد شما به همان اندازه نتایج آن اهمیت دارد.
احتمال نمیرود که اسکریپتی را فقط برای یکبار بنویسید و بعد فراموشش کنید. اگر چنین باشد، باید آنرا اجرا نموده و سپس حذف کنید. اگر خیال دارید استفاده از آن را ادامه دهید،باید طرحی برای نگهداری از آن داشته باشید. برخلاف اطاقتان، کدهای شما نمیتوانند زمان زیادی کثیف باشند، اما شما به طور دائم رویکردها و شیوههای نوینی فرا میگیرید. همچنین بینش جدیدی در خصوص چگونگی کابرد اسکریپت خود به دست میآورید. تمام اطلاعات جدیدی که حین تکمیل کد ابتدایی خود به دست میآورید، باید به طریقی در حفظ و نگهداری کد شمابه کار گرفته شود، که به طور مداوم آن را بهبود بدهد. کد شما بایستی در جهت کاربر محوری و پایداری بیشتر رشد کند.
برای اینکه سالم نگهداری کدهایتان را آسانتر نمایید و به طور مرتب آنها را اصلاح کنید، باید نگاهتان را متوجه خوانایی آنچه مینویسید، بنمایید. وقتی پس از مدت طولانی، به اسکریپتی که از آخرین بازبینی آن یکسال گذشته، باز میگردید و میخواهید آن را اصلاح کنید، یک ویژگی جدید اضافه کنید، یا اشکالی را در آن رفع نمایید، مرا یاد کنید که میگویم ترجیحاً این مورد را ببینید:
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
و بله، میدانم که این مثال کمی اغراقآمیز است، اما من بعضی کدهای معتبری را دیدهام که واقعاً شباهت بسیاری به مثال اخیر دارند.
برای سلامت خودتان این چند نکته را به یاد داشته باشید:
فضای سفید مناسب به شما فضای تنفس میدهد. کدهایتان را به طور صحیح و نامتناقض دندانهدار نمایید. از سطرهای خالی برای جداکردن پاراگرافها یا بلوکهای منطقی استفاده کنید.
از پوشش با کاراکتر
روش تفکرتان را، یادداشت کنید، قبل از اینکه آن را فراموش کنید. ممکن است دریابید، کدی که کاملاً متعارف حس میشود، میتواند موضوع "چه جهنمی فکر میکردم، وقتی این را نوشتم؟" یا "تصور انجام چه کاری از این داشتم؟".
سازگاری از ناراحتی ذهن پیشگیری میکند. در شیوه نامگذاری استوار باشید. در استفاده از حروف بزرگ سازگار باشید. در استفاده خود از ویژگیهای پوسته پایدار باشید. در کدنویسی، برخلاف اطاقخواب، خوبست ساده و قابل پیش بینی باشید.
تفکیک کلمه اهریمن درون 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ها، سطر جدید، یا هر کاراکتر دیگری که در متغیر
$ 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
توجه نمایید که در حلقه
فهرست مطالب
اولین کاری که باید قبل از شروع به نوشتن یک اسکریپت پوسته یا هر نوع اسکریپت یا برنامهای مشابه آن، انجام بدهید، برشمردن احتیاجات و اهداف آن اسکریپت است. سپس ارزیابی آنچه، بهترین ابزار برای انجام آن اهداف است.
BASH ممکن است برای یادگیری و نوشتن در آن آسان باشد، اما همیشه مناسب انجام کار نیست.
در مجموعه ابزارهای اساسی، تعداد بسیاری ابزار موجود است که میتواند به شما کمک کند. اگر شما فقط به AWK نیاز داشته باشید، نباید یک اسکریپت شل ایجاد کنید که آن را فراخوانی کند. فقط یک اسکریپت AWK ایجاد کنید. اگر به بازیابی داده از یک فایل HTMLیا XML به یک روش معتبر نیاز دارید، نیز Bash ابزار اشتباهی برای انجام آن کار است. باید به جای آن XPath/XSLT را به کار ببرید، یا یک زبانی که کتابخانه معتبری برای تجزیه XML یا HTML دارد.
اگر تصمیم گرفتید که اسکریپت پوسته آن ابزاری است که شما میخواهید، اول این سؤالها را از خود بپرسید:
در یک آینده قابل پیشبینی، آیا ممکن است اسکریپت شما در محیطی که Bash به طور پیشفرض در دسترس نیست، مورد احتیاج باشد؟
اگر اینطور است، پس به جای آن sh را در نظر بگیرید. sh یک شل POSIX است و ویژگیهایش در هر پوسته موافق با استاندارد POSIX، در دسترس میباشد. به این واقعیت تکیه کنید که هر سیستم POSIX قادر به اجرای اسکریپت شما خواهد بود. شما باید توازنی بین لزوم قابلیت حمل و عدم استفاده از قابلیتهای ویژه Bash برقرار کنید.
به خاطر داشته باشید که این راهنما شامل sh نمیشود! صفحه bashism پیشنهادهایی دارد، اما کامل نیست.
اگر نه، باید خود را فقط به ویژگیهای Bash 2.x محدود نمایید.
اگر سؤلات فوق انتخاب شما را محدود نمیکند، از تمام ویژگیهای Bash که لازم دارید، استفاده کنید، توجه کنید که کدام نگارش Bash برای اجرای اسکریپت شما لازم است.
استفاده از Bash نگارش 3 یا بالاتر به معنای آنست که میتوانید از شیوههای اسکریپتنویسی کهنه و قدیمی که به دلایل بسیار خوبی با موارد خیلی بهتری جایگزین شدهاند، اجتناب نمایید.
همواره از شبانگ صحیح استفاده کنید. اگر در حال نوشتن اسکریپت هستید، قرار دادن
موقع نوشتن اسکریپتهای پوسته، از دستور [ استفاده نکنید. Bash جایگزین بسیار بهتری دارد:
وقت آنست که `...` نیز به فراموشی سپرده شود. این مورد با ساختار بسط سازگار نیست. به جای آن از
و به واسطه قدرت شگرف، "کاربرد بیشتر نقلقولها!" رشتهها و بسط پارامترهای خود را از تفکیک کلمات محافظت کنید. اگر به طور صحیح نقلقولی نکنید، تفکیک کلمات نوزادان شما را میخورد.
به جای استفاده از sed یا cut برای کار با رشتههای ساده در Bash، استفاده از بسط پارامترها را بیاموزید. اگر میخواهید پسوند نام فایل را حذف کنید، به جای
به جای استفاده از expr برای انجام محاسبات ساده، از حساب داخلی استفاده کنید، مخصوصاً وقتیکه فقط مقدار متغیری افزایش مییابد. اگر اسکریپتی میخوانید که
اگرچه در اسکریپتها معمول نیست، اما کنترل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
پردازش 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های تعلیقی و پسزمینه را لیست میکند. گزینهها شامل
kill میتواند به جای شماره شناسایی، یک مشخصه job قبول کند.
disown به bash میگوید یک job موجود را فراموش کند. این دستور bash را از ارسال خودکار سیگنال SIGHUP به پردازشهای آن job مانع میشود، اما همچنین به معنای آنست که دیگر نمیتوان با jobspec به آن رجوع نمود.
بنابراین، تمام اینها به چه معناست؟ کنترل Job به شما اجازه میدهد در یک نشست منفرد ترمینال، امور چندگانهای در حال اجرا داشته باشید. (در روزگار گذشته، زمانی که شما فقط یک ترمینال روی میز داشتید، و روشی برای ایجاد ترمینالهای مجازی جهت اضافه کردن آن نبود)، بسیار زیاد اهمیت داشت. در سیستمها و سخت افزار مدرن، شما انتخابهای بیشتری در دسترس دارید --برای مثال میتوانید screen یا tmux را برای به دست آوردن ترمینالهای مجازی اجرا نمایید. یا داخل یک نشست X، میتوانید xterm یا شبیهسازهای ترمینال بیشتری باز کنید(و میتوانید این دو را به خوبی باهم ترکیب کنید).
اما گاهی اوقات، یک کنترل job ساده(تعلیق یا پسزمینه) سودمند واقع میشود. شاید یک پشتیبانگیری را شروع کرده باشید و بیش از آنچه انتظار دارید طول بکشد. میتوانید با Ctrl-Z آن را به تعلیق درآورید و سپس آن را با دستور bg در پسزمینه قرار بدهید، واعلان فرمان پوسته را بازپس بگیرید،به طوری که در ضمن اینکه پشتیبانگیری در پسزمینه انجام میشود، بتوانید کار دیگری در همان نشست ترمینال انجام بدهید.
مشخصه job یا "jobspec" روشی برای ارجاع به پردازشی است که job را میسازد. یک مشخصه میتواند چنین باشد:
امکان آن هست که یک دستور اختیاری را بایک jobspec به کار برد، این ساختار: jobs
مطلب انتهایی، یک مشخصه job عریان میتواند به عنوان دستور به کار رود: %1 معادل است با fg
مدیریت پردازش تمرینهایی برای کار با پردازشهای چندتایی را بحث میکند. یک مثال از کاربرد کنترل job در داخل اسکریپت نیز دارد.
وقتی اسکریپتی را از داخل اسکریپت دیگری احضار میکنید، اسکریپت جدید محیط اسکریپت اصلی را به ارث میبرد، درست مانند هر برنامه دیگر اجرا شده در UNIX. تشریح آنکه این به چه معناست، خارج از حوزه این راهنما میباشد، اما بیش از همه، محیط را میتوانید دایرکتوری کاری فعلی، توصیفگرفایلهای باز، ، و متغیرهای محیط که میتوانید آنها را با دستور export ببینید، در نظر بگیرید.
وقتی اسکریپتی که شما اجرا کردهاید(یا هر برنامه دیگری برای آن منظور) اجرایش به پایان میرسد محیطش دور انداخته میشود. محیط اسکریپت اول همان خواهد بود که قبل از فراخوانی اسکریپت دوم بود، اگرچه برخی پارامترهای ویژه bash(از قبیل مقدار متغیر
در پرسش و پاسخهای رایج:
من سعی میکنم اسکریپتی بنویسم که دایرکتوری جاری را تغییر دهد( یا یک متغیر را تنظیم کند)، اما بعد از به پایان رسیدن اسکریپت، در همان جایی هستم که از آنجا شروع کرده بودم(یا متغیر من نیست)!
کاری که میتوانید انجام دهید منبع نمودن اسکریپت درعوض اجرای آن به عنوان پردازش فرزند میباشد. این کار را با استفاده از فرمان
. ./myscript # اجرا میکند myscript فرمانها را در این محیط از فایل
این کار غالباً نقطهای نمودن(dotting in) اسکریپت نامیده میشود. این
توجه کنید که Bashنام دومی برای این دستور دارد، source، اما نظر به اینکه، این عیناً مانند دستور