BashWeaknesses - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

#!/bin/bash

BashWeaknesses

کمبودهای Bash

موارد معینی وجود دارد که در آن موارد BASH خیلی مناسب نیست. برخی وظایف هستند که شما نباید در bash انجام بدهید، مگر اینکه واقعاً، به درستی مجبور باشید. اغلب بهتر است برای انجام اکثر آن وظایف زبان دیگری را انتخاب کنید.

  1. سرعت: آیا ما واقعاً باید آن را بگوییم؟ Bash کُند است. اگر سرعت یک وجه‌التزامِ با اهمیت است، آنوقت ممکن است Bash بهترین انتخاب نباشد.

  2. حساب ممیز شناور: Bash فقط حساب اعداد صحیح دارد. از ‎bc(1)‎ یا در صورتیکه به محاسبه ممیز شناور نیاز دارید از AWK استفاده کنید.

  3. ساختمان داده‌ها: Bash نه رکوردهای سَبکِ پاسکال(ساخت‌های C-شکل) دارد، نه اشاره‌گر دارد. هر تلاشی برای ایجاد ساختمان داده‌های پیشرفته(پشته‌ها، صف‌ها، لیست‌های پیوندی، درختهای دو دویی...) باید با ترفندهای فوق‌العاده قدیمی انجام بشود.

  4. مدیریت پردازش برازنده: Bash مورد قابل قیاس با ‎select(2)‎ یا ‎poll(2)‎ ندارد. راهی برای وارد شدن به یک event loop وجود ندارد. اگر به نمونه فعال شونده در نتیجه رویدادها احتیاج دارید از سایر زبانهای برنامه‌نویسی استفاده نمایید. اکثر زبانهای شئ‌گرا برای انجام این وظایف مناسب‌تر هستند.

  5. تجزیه XML و HTML: (یا مشابه). برای انجام آن به طور صحیح، به ابزارها یا کتابخانه‌های خارجی نیاز دارید. از ‏xslt, tidy, xmlstarlet, پرل، یا برخی ابزارهای مناسب دیگر استفاده کنید.

  6. داده‌های باینری: Bash روشی برای ذخیره بایت تهی در یک متغیر ندارد، بنابراین داده‌های باینری یا باید رمزنگاری(و رمزگشایی) گردند، یا در یک فایل نگهداری شوند. همچنین نمی‌توانید بایت تهی را به عنوان یک شناسه به برنامه عبور بدهید، زیرا کرنل از رشته‌های C برای آنها استفاده می‌کند. تجزیه داده‌های باینری از یک فایل نیز مشکل ناچیزی نیست. به جای آن پرل یا C را امتحان کنید.

  7. پرس و جوی بانک اطلاعاتی: موقع بازیابی یک چندتایی(1 tuple) از یک بانک اطلاعات رابطه‌ای، روشی برای Bash وجود ندارد که بفهمد کجا یک عضو tuple خاتمه می‌یابد و بعدی شروع می‌شود. به طور کلی، Bash برای هیچ نوع بازیابی داده‌ای که در یک عمل منفرد، مقادیر چندتایی داده‌ها را استخراج نماید، مناسب نیست، مگر آنکه به طور آشکار یک جداکننده معین بین فیلدها وجود داشته باشد. برای پرس و جوی بانک اطلاعاتی(SQL یا غیر از آن)، زبان دیگری را که از رابط پرس و جوی بانک اطلاعاتی پشتیبانی نماید، برگزینید.

  8. تعیین نوع متغیر: Bash مانند اکثر زبانهای اسکریپت‌نویسی، به راستی از انواع متغیر نیرومند پشتیبانی نمی‌کند. متغیرها به سستی به عنوان ساده یا آرایه(بعلاوه آرایه‌های انجمنی در ‎bash 4‎)، با پشتیبانی جزئی برای عدد صحیح طبقه‌بندی می‌شوند. اما واقعاً، همه چیز یک رشته است.

  9. اعطای مجوزها: مجاز نمودن یک اسکریپت bash برای اجرا شدن به عنوان کاربر ارشد، می‌تواند دشوار باشد. در زبانهایی مانند C، پرل، و پایتون، شما در یک نقطه معین به آسانی می‌توانید امتیازات را اعطا کنید. در bash این یک مورد ترفندگونه است، زیرا در حالیکه می‌توانید su یا sudo را اجرا کنید، اینها برنامه‌های خارجی هستند -- تمام محیط اجرایی‌تان را از دست می‌دهید.

  10. Try/catch: برخی زبانهای برنامه‌نویسی به شما اجازه می‌دهند فرمانی را در یک بلوک ‎try ... catch‎ بسته‌بندی کنید. این امر فرمان را در نوعی "sandbox" تفسیر می‌کند، جایی که در آن خطاهایی که به طور معمول موجب یک انصراف می‌شدند، گرفتار(caught) می‌شوند، و ماشه اجرای کدِ نوعی مدیریت خطا کشیده می‌شود. Bash چیزی قابل قیاس با این مورد ندارد. هر کدِ bash که شما اجرا می‌کنید، کُد واقعی است.

  11. مدیریت استثناء: بسیاری از زبانهای برنامه‌نویسی دارای مفهومی از یک «استثناء» هستند، در اصل یک رویدادی که موقع وقوع انواع معینی از خطاها، محیطِ حینِ اجرا تولید می‌کند. Bash اینها را ندارد. Bash برای مدیریت خطا مدل C را به کار می‌برد: اجرای آن را به عهده شما می‌گذارد. شما نیاز دارید که نتیجه هر فرمان حساس در اسکریپت خود را کنترل نمایید. ( و خیر، ‎set -e‎ نیز پاسخ مناسب نیست.)

  12. توابع: توابع Bash دارای چند موضوع است:

    • برگشت کمیت‌ها: توابع Bash چیزی برگشت نمی‌دهند ، آنها فقط جریانهای خروجی فراهم می‌کنند. هر روش قابل‌قبولِ به چنگ آوردن آن خروجی و یا تخصیص آن به یک متغیر یا عبور دادن آن به عنوان شناسه مستلزم یک پوسته فرعی است، که تمام تخصیص‌ها را برای حوزه بیرونی نقض می‌کند. برای شگردهای بازیابی نتایج یک تابع، پرسش و پاسخ شماره ۸۴ را ببینید، اما توجه نمایید که تمام آنها ترفند هستند و محدودیت‌های گوناگونی دارند.

    • قابلیت استفاده مجدد: شما نمی‌توانید شناسه‌ها را «با ارجاع» عبور بدهید، یا حداقل تا ‎Bash 4.3‎ نمی‌توانستید (و حتی آنجا هم مکانیسم ‎declare -n‎ دارای نواقص امنیتی جدی است). راهی برای گفتن نام متغیری به تابع که می‌خواهید تابع خروجی‌اش را در آن قرار بدهد وجود ندارد. حتی کار با آرایه‌ها بدتر از آن است -- شما نمی‌توانید نام یک آرایه را به یک تابع عبور داده و بگذارید تابع آن را به کار ببرد. بهترین کاری که می‌توانید انجام بدهید، به طور نمونه، عبور دادن هر عضو آرایه به عنوان یک شناسه جداگانه است. این به معنای آن است که کتابخانه‌های مجتمعِ توابعِ قابل استفادهِ مجدد عملی نیست، مگر به واسطه انجام عملیات آکروباتیک eval.

    • حوزه: Bash دارای یک سیستم ساده حوزه محلی است که تقریباً به حوزه پویا شباهت دارد(به عنوان مثال جاوا اسکریپت، elisp). توابع مناطق فراخواننده‌اشان را می‌بینند( مانند کلمه کلیدی "nonlocal" پایتون)، اما نمی‌توانند پارامتر مکانی فراخواننده را دستیابی کنند (مگر از طریق BASH_ARGV در صورتی که extdebug فعال بشود). معاف شدن توابع قابل استفاده مجدد از تداخل‌های namespace نمی‌تواند تضمین شود مگر برای آنکه برخوردها به قدر کفایت غیر محتمل گردند، شما به قواعد نامگذاری عجیب متوسل شوید. این مطلب مخصوصاً در صورتی یک مسئله است که اجرای توابعی مورد انتظار باشد که از قاب n-3 بر روی نام متغیرهایی عمل کند که توسط تابع قابل استفاده مجدد شما در قاب n-2 رونویسی گردیده است. Ksh93 می‌تواند از قواعد حوزه لغوی بیشتر متداول، به وسیله تعریف توابع با ترکیب دستوری ‎"function name { ... }"‎ استفاده نماید (Bash نمی‌تواند، امابه هر حال از این ترکیب پشتیبانی می‌کند).

    • Closureها 2: در Bash، توابع خودشان همیشه سراسری هستند(دارای حوزه فایل)، بنابراین closure نیستند. تعاریف تابع ممکن است تو در تو باشد، اما اینها closure نیستند، هرچند خیلی زیاد مشابه آنها هستند. توابع «گذر پذیر» نیستند(درجه اول)، و هیچ تابع بدون نامی وجود ندارد (lambdas). در حقیقت، چیزی قابل عبور نیست، مخصوصاً آرایه‌ها. Bash معناشناسی سخت‌گیرانه call-by-value را به کار می‌برد .

    • پیچیدگی‌های بسیاری بیشتری را شامل می‌گردد، پوسته‌های فرعی، توابع صادر شده، «function collapsing» (توابعی که خودشان یا سایر توابع را تعریف یا بازتعریف می‌کنند)، traps (و میراث آنها)، و روشی که توابع با ورودی خروجی استاندارد فعل و انفعال می‌کنند. نوآموز را برای عدم درک همه این موارد نیش نزنید. به طور کلی توابع پوسته f***ed(در وضعیت نامناسب) هستند.

  13. مرتب سازی: Bash نمی‌تواند گروههای داده‌ها را مرتب نماید. اگر نیاز دارید یک آرایه را مرتب کنید، یا می‌توانید الگوریتم مرتب‌سازی خودتان را در bash خالص بنویسید، یا می‌توانید مجموعه داده‌ها را مسلسل نموده آن را به sort لوله‌کشی کنید و سپس آنرا تجزیه و برگردانید. هر یک از این روشها آزاردهنده است، مخصوصاً اگر برنامه sort شما گزینه ‎-z‎ نداشته باشد.

برتر از تمام اینها، BASH برای برنامه‌های بزرگ مطلوب نیست. اگر برنامه شما می‌خواهد پاسخگوی وظایف زیادی، بویژه به طور فعل و انفعالی باشد، آنوقت شاید لازم باشد مفسر دیگری را در نظر بگیرید یا به طور کلی به سمت یک زبان قابل ترجمه بروید. اسکریپت‌های بزرگ Bash خیلی به سرعت رنجش‌آور می‌شوند زیرا Bash در بسیاری از مواردی که مفسرهای دیگر در آن سریع هستند، کند است. با کاربرد معدود روشهای غیر از توابع به منظور ایجاد ساختار در کدتان، قطعات بزرگ کدِ Bash به سرعت غیر شفاف می‌گردند. اسکریپت‌های Bash تقریباً غیر قابل تست هستند. حتی آن برنامه‌نویسان bash که بیشترین وسواس را در استعمال صحیح کلمات دارند ( و زیاد هم نیستند ) کدی می‌نویسند که وقتی تمام آن جمع می‌شود، برای نگهداری مشکل می‌گردد. Bash برای ایمنی کدی که اجازه می‌دهد باگهای آب زیر کاه واقعاً به سادگی بدون اطلاع یا هشدار در آن رسوخ کنند، تقریباً راه‌کاری ندارد. و موقعی که امور اشتباه می‌شوند(و امور اشتباه خواهند شد)، به راستی اشکالزدایی اسکریپت‌های بزرگ خیلی دشوار است.

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


CategoryShell

کمبودهای Bash (آخرین ویرایش ‎2013-06-28 20:51:14‎ توسط GreyCat)


  1. مترجم: tuple یک مورد داده‌‌ای متشکل از دو عنصر یا بیشتر است، مجموعه‌ای از مقادیر مرتبط با هم که در سیستم مدیریت رابطه‌ای در یک سطر ذخیره می‌شود، یا مجموعه‌ای از صفات مشخصه که نشانگر یک موجودیت متناظر با یک سطر از یک جدول هستند (بازگشت)

  2. مترجم: closure یک تکنیک برنامه‌نویسی است که اجازه می‌دهد متغیرهای خارج از حوزه یک تابع دستیابی گردند، در بسیاری از موقعیت‌ها یک closure هنگامی ایجاد می‌شود که تابعی داخل تابع دیگر تعریف می‌گردد، و به تابع داخلی اجازه می‌دهد که به متغیرهای تابع خارجی دسترسی داشته باشد. (بازگشت)