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

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

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

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

هرگز این کارها را نکنید

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


5. هرگز این موارد را انجام ندهید

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

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

برای پاکیزگی اسکریپت‌هایتان، و به خاطر تمام افراد بشر، هرگز هیچ موردی از سطور زیر را انجام ندهید:

  • ls -l | awk '{ print $8 }'

    • هرگز تجزیه خروجی فرمان ls را انجام ندهید! خروجی فرمان ls به چند دلیل نمی‌تواند قابل اعتماد باشد.

      1. اول، اگر نام فایلها شامل کاراکترهای پشتیبانی نشده زبان محلی شما باشد، ls نامها را خُرد خواهد نمود. در نتیجه، خروجی حاصل از تجزیه نام فایلها توسط ls، هرگز تضمین نمی‌شود که واقعاً همان نامهایی که شما قادر به یافتن آنها می‌باشید را به شما بدهد. ls ممکن است بعضی کاراکترها در نام فایل را با کاراکتر علامت سؤال تعویض نماید.

      2. دوم، ls سطرهای داده‌ها را بر اساس کاراکتر سطر جدید تفکیک می‌کند. به این طریق، هر تکه از اطلاعات یک فایل در یک سطر است. متأسفانه، نام فایلها نیز خودشان می‌توانند شامل سطر جدید باشند. این به معنی آنست که اگر شما فایلی در دایرکتوری جاری با نام شامل کاراکتر سطر جدید داشته باشید، کاملاً نتیجه تجزیه شما را درهم می‌ریزد و اسکریپت شکست می‌خورد!

      3. آخر از همه، اما نه کم اهمیت‌تر، قالب خروجی فرمان ‎ ls -l‎ تضمین نمی‌شود که در تمام پلاتفرم‌ها همسان باشد. برخی سیستم‌ها به طور پیش‌فرض شماره شناسایی گروه را از قلم می‌اندازند، و اثر گزینه ‎-g‎ را معکوس می‌نمایند. بعضی سیستم‌ها از دو فیلد برای زمان ویرایش و برخی از سه فیلد برای آن استفاده می‌کنند. در سیستم‌هایی که از سه فیلد استفاده می‌کنند، فیلد سوم می‌تواند سال یا یک ‎ HH:MM‎ الحاقی، نسبت به سن فایل، باشد.

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

  • if echo "$file| fgrep .txt; then
    ls *.txt | grep story

    • هرگز نام فایل ‌ها را با grep بررسی یا فیلتر نکنید! غیر از آنکه الگوی grep شما واقعاً هوشمند باشد، این کار احتمالاً قابل اطمینان نخواهد بود.

    • در نمونه اول مثال فوق، بررسی با هر دو مورد story.txt و story.txt.exe منطبق می‌گردد. اگر الگوهایی از grep ایجاد کنید که به اندازه کافی هوشمند باشند، احتمالاً آنها به قدری زشت، حجیم و ناخوانا می‌شوند، که با این وجود نباید از آنها استفاده کنید.

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

  • cat file | grep pattern

    • برنامه cat را برای خوراندن محتویات یک فایل منفرد به یک فیلتر به کار نبرید. cat یک ابزار مورد استفاده برای الحاق محتویات چند فایل با یکدیگر است.

    • برای تغذیه محتویات فایلی به یک پردازش، احتمالاً می‌توانید نام فایل را به عنوان شناسه تحویل برنامه مورد نظر(مانند ‎grep 'pattern/my/file‎ یا ‎sed 'expression/my/file ‎و غیره) بدهید.

    • اگر مستندات برنامه هیچ راهی برای انجام این کار تعیین نکرده است، باید از تغییر مسیر استفاده کنید (‎read column1 column2 < /my/file‎ یا ‎tr ' ' '\n< /my/file ‎و غیره).

  • for line in $(<file); do

    • از حلقه for برای خواندن سطرهای یک فایل استفاده نکنیم. به جای آن حلقه while read را به کار ببریم.

  • for number in $(seq 1 10); do

    • به خاطر خدا و به خاطر تمام مقدسات، از برنامه seq برای شمارش استفاده نکنید.

    • Bash به اندازه کافی در انجام شمارش توانمند است. نیازی به یک برنامه خارجی(مخصوصاً یک برنامه تک سکویی) برای انجام محاسبه و ارسال آن به خروجی Bash جهت تفکیک کلمه، ندارید. ترکیب دستوری for را قبلاً آموخته‌اید!

    • باید در Bash نگارش 3 به بعد، از این: ‎for number in {1..10}‎، یا در ‎Bash 2‎ از این: ‎for ((i=1i<=10i++))‎ استفاده کنید.

    • اگر شما عملاً یک جریانی از اعداد که با کاراکتر سطر جدید از هم جدا شده‌اند، هنگام بررسی ورودی می‌خواهید، این مورد را در نظر بگیرید: ‎printf '%d\n' {1..10}

  • i=`expr $i + 1>`

    • expr یک عتیقه رُم باستان است. آن را به کار نبرید.

    • این برنامه در اسکریپت‌های نوشته شده برای پوسته‌هایی با امکانات بسیار محدود، به کار می‌رفت. اساساً با استفاده از آن در حال ایجاد یک پردازش جدید هستید که برنامه C دیگری برای انجام برخی محاسبات را برایتان فراخوانی نماید و نتایج را به صورت رشته به bashتحویل بدهد. Bash تمام اینها را خودش می‌تواند خیلی سریعتر، و به طور قابل اعتمادتر (بدون تبدیل عدد به ->رشته -> به عدد) و در همه حال بهتر، انجام بدهد.

    • شما در Bash باید از این استفاده کنید: ‎let i++ ‎ یا ‎((i++))

    • حتی پوسته POSIX بورن می‌تواند محاسبات را انجام بدهد: i=$(($i+1))‎. فقط فاقد عملگر ++ و دستور‎ ((...))‎ می‌باشد(فقط عبارت جایگزینی ‎ $((...))‎ را دارد).


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

تست‌های 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 قابلیت حمل آن است.


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