راهنمای آموزشی اسکریپت نویسی - مقدماتی

لطفا برای بهتر دیدن صفحه از firefox استفاده کنید
Bash Guide for Beginners Machtelt Garrels

راهنمای Bash برای نوآموزان

فصل ۷ - جملات شرطی »
« کاربرد دستور case

کاربردهای پیشرفته‌تر if

‎ ساختارهای if/then/else‎

مثال نمونه

این ساختار مورد استفاده برای انجام یک مجموعه فعالیت در صورتی که نتیجه بررسی if صحیح باشد، و مجموعه دیگر در صورت غلط بودن آن، می‌باشد. یک مثال:

freddy scripts> gender="male"

freddy scripts> if [[ "$gender" == f* ]]
More input> then echo "Pleasure to meet you, Madame."
More input> else echo "How come the lady hasn't got a drink yet?"
More input> fi
How come the lady hasn't got a drink yet?

freddy scripts>

[Important][] vs. [[]]

بر خلاف [، به کار بردن [[ از تفکیک کلمه محتوای متغیر پیش‌گیری می‌نماید. بنابراین، اگر‎ VAR="var with spaces"‎ باشد، نیازی نیست ‎ $VAR‎ را در یک بررسی در نقل‌قول دوگانه قرار دهید -هرچندکه استفاده از نقل‌قول‌ها یک عادت خوب است. همچنین، ‎ [[‎ از بسط نام مسیر هم جلوگیری می‌کند، بنابراین سعی نمی‌شود رشته‌های شامل کاراکترهای عام به نام فایل‌ها بسط داده شوند. کاربرد‎ [[‎ و ‎ ==‎ یا ‎ !=‎ رشته‌های سمت راست رابه عنوان الگوهای جانشینی شل که باید در مقابل مقدار سمت چپ مطابقت داده شوند، تفسیر می‌کنند، به عنوان نمونه: ‎ [[ "value" == val* ]]‎ .

همانند CONSEQUENT-COMMANDS یعنی لیست متعاقب جمله then، لیست ALTERNATE-CONSEQUENT-COMMANDS متعاقب جمله else نیز می‌تواند هر دستور یونیکسی که یک کد وضعیت خروج صادرکند، را در بر داشته باشد.

یک مثال دیگر، توسعه یافته مثال بخشی به نام «بررسی وضعیت خروج»:

anny ~> su -
Password:
[root@elegance root]# if ! grep ^$USER /etc/passwd 1> /dev/null
> then echo "your user account is not managed locally"
> else echo "your account is managed from the local /etc/passwd file"
> fi
your account is managed from the local /etc/passwd file
[root@elegance root]#

ما برای نمایش دادن تأثیر جمله else به حساب کاربر root تغییر وضعیت داده‌ایم - root شما به طور معمول حساب محلی است در حالیکه حساب کاربری خودتان ممکن است توسط یک سیستم مرکزی از قبیل یک سرویس دهنده LDAP مدیریت گردد.

بررسی شناسه‌های خط‌فرمان

به جای آنکه متغیری را تنظیم کرده وسپس یک اسکریپت را اجرا کنیم، به مراتب پسندیده‌تر است کمیت‌های متغیرها را در خط فرمان به آن بدهیم.

ما از پارامترهای موقعیتی( یا موضعی ) ‎ $1‎ ، ‎ $2‎ ، ...، ‎ $N‎ برای این مقصود استفاده می‌کنیم. متغیر ‎ $#‎ به تعداد شناسه‌های خط‌فرمان ارجاع می‌دهد . متغیر ‎ $0‎ به نام اسکریپت ارجاع می‌دهد .

مثال زیر یک نمونه ساده است:

شکل  7.1: بررسی یک شناسه خط‌فرمان با if

Simple if/then/else/fi construct: if [ "$1" == fish ]; then echo "Tux likes this"; else echo "Tux wants fish!"; fi

این هم یک مثال دیگر با استفاده از دو شناسه:

anny ~> cat weight.sh
#!/bin/bash

# This script prints a message about your weight if you give it your
# weight in kilos and height in centimeters.

weight="$1"
height="$2"
idealweight=$[$height - 110]

if [ $weight -le $idealweight ] ; then
  echo "You should eat a bit more fat."
else
  echo "You should eat a bit more fruit."
fi

anny ~> bash -x weight.sh 55 169
+ weight=55
+ height=169
+ idealweight=59
+ '[' 55 -le 59 ']'
+ echo 'You should eat a bit more fat.'
You should eat a bit more fat.

بررسی تعداد شناسه‌ها

مثال زیر نشان می‌دهد که چگونه اسکریپت قبلی را طوری تغییر دهیم که اگر کمتر یا بیشتر از دو شناسه داده شود، یک پیغام را چاپ نماید:

anny ~> cat weight.sh
#!/bin/bash

# This script prints a message about your weight if you give it your
# weight in kilos and height in centimeters.

if [ ! $# == 2 ]; then
  echo "Usage: $0 weight_in_kilos length_in_centimeters"
  exit
fi

weight="$1"
height="$2"
idealweight=$[$height - 110]

if [ $weight -le $idealweight ] ; then
  echo "You should eat a bit more fat."
else
  echo "You should eat a bit more fruit."
fi

anny ~> weight.sh 70 150
You should eat a bit more fruit.

anny ~> weight.sh 70 150 33
Usage: ./weight.sh weight_in_kilos length_in_centimeters

به اولین شناسه توسط‎ $1‎ رجوع می‌شود، به دومی با‎ $2‎ و به همین ترتیب. تعداد کل شناسه‌ها در متغیر ‎ $#‎ نگهداری می‌شود.

بخشی به نام « کاربرد دستور exit و if » را برای روش دلپستدتر چاپ پیغام‌ها، ملاحظه کنید.

کنترل وجود یک فایل

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

#!/bin/bash

# This script gives information about a file.

FILENAME="$1"

echo "Properties for $FILENAME:"

if [ -f $FILENAME ]; then
  echo "Size is $(ls -lh $FILENAME | awk '{ print $5 }')"
  echo "Type is $(file $FILENAME | cut -d":" -f2 -)"
  echo "Inode number is $(ls -i $FILENAME | cut -d" " -f1 -)"
  echo "$(df -h $FILENAME | grep -v Mounted | awk '{ print "On",$1", \
which is mounted as the",$6,"partition."}')"
else
  echo "File does not exist."
fi

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

[Tip]فاصله‌ها در نام فایل‌ها

اسکریپت مثال فوق در صورتی که مقدار متغیر ‎ $1‎ بتواند به چند کلمه تفکیک شود، با شکست مواجه می‌شود. در آن حالت، دستورif می‌تواند با قرار دادن نقل‌قول در اطراف نام‌فایل، و یا بااسفاده از ‎ [[‎ به جای‎ [‎ رفع نقص شود.

ساختارهای ‎if/then/elif/else ‎

کلیات

این شکل کاملی از فرمان if می‌باشد:

if TEST-COMMANDS; then

CONSEQUENT-COMMANDS;
elif MORE-TEST-COMMANDS; then
MORE-CONSEQUENT-COMMANDS;
else ALTERNATE-CONSEQUENT-COMMANDS;
fi

لیست TEST-COMMANDS اجرا می‌شود و در صورتیکه کد برگشتی صفر باشد، لیست CONSEQUENT-COMMANDS اجرا می‌شود. اگر TEST-COMMANDS یک کد وضعیت غیر صفر بازگرداند، هر لیست elif یکی پس از دیگری اجرا می‌شود، و اگر کد خروج آن صفر باشد، لیست MORE-CONSEQUENT-COMMANDS متناظر آن اجرا می‌شود و دستور if کامل می‌شود. اگر در ادامه جمله else با یک لیست ALTERNATE-CONSEQUENT-COMMANDS حضور داشته باشد، و دستور نهایی در if یا elif انتهایی منجر به یک کد خروج غیر صفر گردد، بعد ALTERNATE-CONSEQUENT-COMMANDS اجرا می‌شود. کد برگشتی، کد خروج آخرین دستور اجرا شده، یا اگر شرط بررسی شده صحیحی نباشد، صفر است.

مثال

این یک نمونه است که می‌توانید برای اجرای روزانه در فایل crontab خودتان قرار دهید:

anny /etc/cron.daily> cat disktest.sh
#!/bin/bash

# This script does a very simple test for checking disk space.

space=`df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -`
alertvalue="80"

if [ "$space" -ge "$alertvalue" ]; then
  echo "At least one of my disks is nearly full!" | mail -s "daily diskcheck" root
else
  echo "Disk space normal" | mail -s "daily diskcheck" root
fi

جملات if تو در تو

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

این یک مثال جهت بررسی سالهای کبیسه است:

anny ~/testdir> cat testleap.sh
#!/bin/bash
# This script will test if we're in a leap year or not.

year=`date +%Y`

if [ $[$year % 400] -eq "0" ]; then
  echo "This is a leap year.  February has 29 days."
elif [ $[$year % 4] -eq 0 ]; then
        if [ $[$year % 100] -ne 0 ]; then
          echo "This is a leap year, February has 29 days."
        else
          echo "This is not a leap year.  February has 28 days."
        fi
else
  echo "This is not a leap year.  February has 28 days."
fi

anny ~/testdir> date
Tue Jan 14 20:37:55 CET 2003

anny ~/testdir> testleap.sh
This is not a leap year.

عملگرهای منطقی

مثال بالا می‌تواند با به کار گیری عملگرهای منطقی “AND” (&&) و “OR” (||) کوتاه شود.

شکل 7.2: مثال استفاده از عملگرهای منطقی

year=`date +%Y`; if (( ("$year" % 400) == "0" )) || (( ("$year" % 4 == "0") && ("$year" % 100 != "0") )); then echo "this is a leap year."; else echo "not a leap year"; fi

ما برای بررسی یک عبارت حسابی ازپرانتزهای دوتایی استفاده کرده‌ایم، بخشی به نام« عبارت حسابی» را ملاحظه کنید. این معادل فرمان letدر اینجا، اگر شما چیزی مشابه ‎$[$year % 400]‎ را بیازمایید، کروشه‌ها را به صورت چسبیده خواهید داشت، به علت آنکه در اینجا آنها یک دستور واقعی را نمایندگی نمی‌کنند.

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

کاربرد فرمان exit و if

ما قبلاً به طور مختصر با فرمان exit در بخشی به نام«بررسی تعداد شناسه‌ها» برخورد کردیم. این فرمان اجرای تمام اسکریپت را خاتمه می‌دهد. این اکثراً موقعی که ورودی درخواستی از کاربر غلط باشد، یک دستور به طور ناموفقی اجرا شود یا در صورت وقوع برخی خطاهای دیگر به کار می‌رود.

دستور exit یک شناسه اختیاری نیز می‌پذیرد. این شناسه یک عدد صحیح کد وضعیت خروج می‌باشد، که به والد برگردانیده می‌شود و در متغیر‎ $?‎ ذخیره می‌شود.

یک شناسه صفر به این معنی است که اسکریپت به طور موفقی اجرا شده. هر مقدار دیگری می‌تواند توسط برنامه‌نویس برای بازگرداندن پیغام‌های متفاوت به والد استفاده شود، به طوری که فعالیت‌های متفاوتی نسبت به موفقیت یا شکست پردازش فرزند، می‌تواند انجام شود. اگر فرمان exit هیچ شناسه‌ای نداشته باشد، پوسته والد مقدار جاری متغیر ‎$? ‎  را به کار می‌برد.

در پایین یک مثال با تغییرات اندکی در اسکریپت penguin.sh، که کد خروجش را به اسکریپت والدش، feed.sh باز می‌گرداند:

anny ~/testdir> cat penguin.sh
#!/bin/bash
                                                                                                 
# This script lets you present different menus to Tux.  He will only be happy
# when given a fish.  We've also added a dolphin and (presumably) a camel.
                                                                                                 
if [ "$menu" == "fish" ]; then
  if [ "$animal" == "penguin" ]; then
    echo "Hmmmmmm fish... Tux happy!"
  elif [ "$animal" == "dolphin" ]; then
    echo "Pweetpeettreetppeterdepweet!"
  else
    echo "*prrrrrrrt*"
  fi
else
  if [ "$animal" == "penguin" ]; then
    echo "Tux don't like that.  Tux wants fish!"
    exit 1
  elif [ "$animal" == "dolphin" ]; then
    echo "Pweepwishpeeterdepweet!"
    exit 2
  else
    echo "Will you read this sign?!"
    exit 3
  fi
fi

این اسکریپت در سطحی بالاتر از دیگری فراخوانی شده، بنابراین متغیرهای menu و animal را صادر می‌کند:

anny ~/testdir> cat feed.sh
#!/bin/bash
# This script acts upon the exit status given by penguin.sh
                                                                                                 
export menu="$1"
export animal="$2"
                                                                                                 
feed="/nethome/anny/testdir/penguin.sh"
                                                                                                 
$feed $menu $animal
                                                                                                 
case $? in
                                                                                                 
1)
  echo "Guard: You'd better give'm a fish, less they get violent..."
  ;;
2)
  echo "Guard: It's because of people like you that they are leaving earth all the time..."
  ;;
3)
  echo "Guard: Buy the food that the Zoo provides for the animals, you ***, how
do you think we survive?"
  ;;
*)
  echo "Guard: Don't forget the guide!"
  ;;
esac
                                                                                                 
anny ~/testdir> ./feed.sh apple penguin
Tux don't like that.  Tux wants fish!
Guard: You'd better give'm a fish, less they get violent...

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

فصل ۷ - جملات شرطی »
« کاربرد دستور case
ترجمه محمود پهلوانی