خط فرمان لینوکس

ترجمه فارسی LinuxCommand.org

خط فرمان لینوکس

ترجمه فارسی LinuxCommand.org

کنترل جریان - بخش 1

کنترل جریان - بخش 1

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

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

if

اولین فرمانی که بررسی خواهیم کرد if است. فرمان if در ظاهر تا اندازه‌ای ساده است، بر اساس وضعیت خروجِ یک فرمان تصمیمی می‌گیرد. فرمان if دارای ترکیب دستوری زیر است:

if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi

که در آن commands لیستی از فرمانها است. در نگاه اول این کمی مغشوش کننده است. اما پیش از آن که ما بتوانیم آن را واضح کنیم، باید نگاه کنیم که پوسته چطور موفقیت یا شکست یک فرمان را ارزیابی می‌کند.

وضعیت خروج

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

[me@linuxbox ~]$ ls -d /usr/bin
/usr/bin
[me@linuxbox ~]$ echo $?
0
[me@linuxbox ~]$ ls -d /bin/usr
ls: cannot access /bin/usr: No such file or directory
[me@linuxbox ~]$ echo $?
2

در این مثال، ما فرمان ls را دوبار اجرا می‌کنیم. دفعه اول، فرمان موفقیت‌آمیز اجرا می‌گردد. اگر ما مقدار پارامتر ‎$?‎ را نمایش بدهیم، مشاهده می‌کنیم که صفر است. فرمان ls را نوبت دوم اجرا می‌کنیم، یک خطا تولید می‌کند و دوباره پارامتر ‎$?‎ را بازپرسی می‌کنیم. این دفعه محتوی 2 است، نشان‌دهنده آن که فرمان با یک خطا مواجه گردیده است. بعضی فرمانها مقادیرِ وضعیتِ خروجِ متفاوتی به منظورِ میسر کردن عیب‌شناسی خطاها به کار می‌برند، در حالیکه بسیاری از فرمانها وقتی ناموفق می‌شوند به سادگی با یک مقدار 1 خارج می‌گردند. صفحه‌های man اغلب بخشی با عنوان «وضعیت خروج» در توضیح این که کدام کُدها استفاده می‌شوند ضمیمه می‌کنند. در هر صورت صفر همیشه بیانگر موفقیت است.

پوسته دو فرمان به شدت سادهِ داخلی فراهم می‌کند که کاری انجام نمی‌دهند غیر از اینکه به یک وضعیت خروج صفر یا یک منتهی می‌شوند. فرمان true همیشه به طور موفقیت‌آمیز اجرا می‌گردد و فرمان false همواره به طور ناموفق اجرا می‌شود:

[me@linuxbox~]$ true
[me@linuxbox~]$ echo $?
0
[me@linuxbox~]$ false
[me@linuxbox~]$ echo $?
1

ما می‌توانیم از این فرمانها برای مشاهده آنکه جمله if چطور کار می‌کند استفاده نماییم. آنچه جمله if واقعاً انجام می‌دهد ارزیابی موفقیت یا شکست فرمانها می‌باشد:

[me@linuxbox ~]$ if true; then echo "It's true."; fi
It's true.
[me@linuxbox ~]$ if false; then echo "It's true."; fi
[me@linuxbox ~]$

فرمان ‎echo "It's true."‎ موقعی اجرا می‌گردد که فرمان بعد از if به طور موفق اجرا می‌شود، و هنگامی که اجرای فرمان بعد از if موفقیت‌آمیز نیست اجرا نمی‌گردد.

test

اکثر اوقات فرمان test با فرمان if برای انجام دادن داوری‌های صحیح-غلط به کار می‌رود. فرمانی غیر عادی است از این حیث که دارای دو شکل دستوری متفاوت است:

# شکل اول‎

test expression

# شکل دوم‎

[ expression ] 

فرمان test به طور ساده کار می‌کند. اگر عبارت داده شده صحیح باشد، test با وضعیت خروج صفر خارج می‌شود، در غیر اینصورت با یک وضعیت 1 خارج می‌گردد.

ویژگی پاکیزه test در تنوع عبارت‌هایی است که شما می‌توانید تولید کنید. این هم یک مثال:

if [ -f .bash_profile ]; then
    echo "You have a .bash_profile. Things are fine."
else
    echo "Yikes! You have no .bash_profile!"
fi 

مترجم: اگر شما فرمان echo را با شناسه محتوی کاراکتر‌ ! در پوسته محاوره‌ای به کار ببرید با خطایی مانند ‎bash: !": event not found‎ مواجه می‌شوید. توضیح کامل

در این مثال، ما عبارت ‎" -f .bash_profile "‎ را به کار می‌بریم. این عبارت می‌پرسد، «آیا ‎.bash_profile‎ یک فایل است؟» اگر عبارت صحیح باشد، آنوقت test با یک صفر (بیانگر صحیح) خارج می‌شود و فرمان if دستور(ات) پس از کلمه then را اجرا می‌کند. در صورتیکه عبارت غلط باشد، آنوقت test با وضعیت یک خارج می‌شود و فرمان if دستور(ات) بعد از کلمه else را اجرا می‌کند.

این هم یک فهرست نیمه کامل از شرایطی که test می‌تواند ارزیابی نماید. چون test یک builtin پوسته است، از‎"help test"‎ برای دیدن فهرست کامل استفاده نمایید.

عبارت توضیح

-d file

در صورتیکه file یک دایرکتوری باشد صحیح است.

-e file

اگر file موجود باشد صحیح است.

-f file

اگر file موجود و یک فایل معمولی باشد صحیح است.

-L file

اگر file یک پیوند نمادین باشد صحیح است.

-r file

اگر file یک فایل قابل خواندن برای شما باشد صحیح است.

-w file

صحیح در صورتیکه file فایل برای شما قابل نوشتن باشد.

-x file

اگر file برای شما قابل اجرا باشد صحیح است.

file1 -nt file2

اگر file1 جدیدتر از (طبق زمان ویرایش) file2 باشد صحیح است.

file1 -ot file2

صحیح اگر file1 قدیمی‌تر از file2 باشد

-z string

در صورتی صحیح است که string تهی باشد.

-n string

صحیح اگر string تهی نباشد.

string1 = string2

اگر string1 معادل string2 باشد صحیح است.

string1 != string2

اگر string1 معادل string2 نباشد صحیح است.

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

در سطر نخست از اسکریپت، فرمان if را می‌بینیم که به وسیله فرمان test ادامه یافته، و با یک semicolon دنبال می‌گردد، و سرانجام کلمه then. من استفاده از ‎[ expression ]‎ را برای فرمان test انتخاب کردم چون اکثر اشخاص خواندن آن را آسانتر می‌دانند. توجه نمایید که فاصله مابین ‎"["‎ و شروعِ عبارت ضروری است. به همچنین، فاصله بین انتهای عبارت و ‎ "]"‎ پس از آن لازم است.

کاراکتر ; یک جداکننده فرمان است. کاربرد آن به شما اجازه می‌دهد بیش از یک فرمان را در یک سطر قرار بدهید. برای مثال:

[me@linuxbox me]$ clear; ls

صفحه را پاک کرده و فرمان ls را اجرا خواهد نمود.

من از semicolon استفاده می‌کنم چون به من اجازه می‌دهد کلمه then را در همان سطر فرمان if قرار بدهم، زیرا من گمان می‌کنم به این ترتیب برای خواندن آسانتر است.

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

به بیان دیگر، ما می‌توانستیم به صورت زیر بنویسیم و همان نتایج را به دست می‌آوریم:

# شکل جایگزین‎

if [ -f .bash_profile ]
then
    echo "You have a .bash_profile. Things are fine."
else
    echo "Yikes! You have no .bash_profile!"
fi

# یک شکل جایگزین دیگر‎

if [ -f .bash_profile ]
then echo "You have a .bash_profile. Things are fine."
else echo "Yikes! You have no .bash_profile!"
fi 

exit

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

exit 0 

اسکریپت شما خارج می‌شود و وضعیت خروج را به صفر (موفقیت) تنظیم می‌کند، در حالیکه با

exit 1 

اسکریپت شما خارج می‌شود و وضعیت خروج را به 1 (شکست) تنظیم می‌کند.

بررسی برای کاربر ارشد

وقتی آخرین بار اسکریپت خود را ترک کردیم، نیاز داشتیم که با مزایای کاربر ارشد اجرا گردد. چنین است زیرا تابع home_space به بررسی کردن اندازه دایرکتوری خانگی هر کاربر احتیاج دارد، و تنها کاربر ارشد اجازه انجام آن را دارد.

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

فرمان id می‌تواند به ما بگوید چه کسی کاربر جاری است. موقعی که با گزینه ‎-u‎ به کار برود، شماره شناسایی کاربر جاری را چاپ می‌کند.

[me@linuxbox me]$ id -u
501
[me@linuxbox me]$ su
Password:
[root@linuxbox me]# id -u
0

اگر کاربر ارشد ‎id -u‎ را اجرا کند، فرمان ‎"0"‎ را بیرون می‌دهد. این واقعیت می‌تواند اساس بررسی ما قرار گیرد:

if [ $(id -u) = "0" ]; then
    echo "superuser"
fi
       

در این مثال، در صورتیکه خروجی فرمان ‎id -u‎ معادل رشته ‎"0"‎ باشد، آنوقت رشته ‎"superuser"‎ چاپ می‌شود.

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

if [ $(id -u) != "0" ]; then
    echo "You must be the superuser to run this script" >&2
    exit 1
fi 

با این کد، اگر خروجی فرمان ‎id -u‎ معادل با ‎"0"‎ نباشد، آنوقت اسکریپت یک پیغام خطای وصفی چاپ کرده، خارج می‌شود، و برای نشان دادن آنکه اسکریپت به طور ناموفق اجرا گردیده وضعیت خروج را به 1 تنظیم می‌کند.

به ‎">&2"‎ در انتهای فرمان echo توجه نمایید. این شکل دیگری از تغییر مسیر ورودی-خروجی است. بیشتر اوقات شما این مورد را در روال‌هایی که پیغام‌های خطا نمایش می‌دهند ملاحظه خواهید نمود. اگر این تغییر مسیر انجام نمی‌شد، پیغام خطا به خروجی استاندارد می‌رفت. با این تغییر مسیر پیغام به خطای استاندارد فرستاده شد. چون ما در حال اجرای اسکریپت و فرستادن خروجی استانداردش به یک فایل هستیم، خواستار پیغام خطای جداشده از خروجی معمولی هستیم.

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

function home_space
{
    # فقط کاربر ارشد می‌تواند این اطلاعات را به دست بیاورد‎

    if [ "$(id -u)" = "0" ]; then
        echo "<h2>Home directory space by user</h2>"
        echo "<pre>"
        echo "Bytes Directory"
            du -s /home/* | sort -nr
        echo "</pre>"
    fi

}   # end of home_space 

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