فصل ‎11‎- حلقه‌ها و انشعاب‌ها

‎11.4‎- بررسی و انشعاب

ساختارهای case و select از نظر تکنیکی حلقه نیستند، چون آنها اجرای یک قطعه کد را تکرار نمی‌کنند. اگر چه، آنها نیز جریان برنامه را بر حسب شرایط ابتدا یا انتهای قطعه کد هدایت می‌کنند.

کنترل جریان برنامه در یک قطعه کد

‎case (in) / esac‎

ساختار case در اسکریپت‌نویسی پوسته قابل قیاس با switch در ‎C/C++‎ است. این ساختار بر اساس وضعیت بررسی‌ها، انشعاب به یکی از چند قطعه کد را اجازه می‌دهد. به عنوان یک نوع میانبر برای چندین جمله ‎if/then/else‎ خدمت می‌کند و ابزار مناسبی برای ایجاد منوها است.

case "$variable" in

"$condition1" )
command...
;;

"$condition2" )
command...
;;


esac

  • نقل‌قول متغیرها الزامی نیست، چون تفکیک کلمه صورت نمی‌گیرد.

  • هر سطر تست با یک پرانتز سمت راست ‎)‎ خاتمه می‌یابد. ‎[1]‎

  • هر بلوک شرط با یک سمی‌کالن دوگانه ;; خاتمه می‌یابد.

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

  • کل بلوک case با یک esac ( املای معکوس case) پایان می‌پذیرد.


مثال ‎11-25‎. کاربرد case

#!/bin/bash
# .بررسی محدوده کاراکترها

echo; echo "Hit a key, then hit return."
read Keypress

case "$Keypress" in
  [[:lower:]]   ) echo "Lowercase letter";;
  [[:upper:]]   ) echo "Uppercase letter";;
  [0-9]         ) echo "Digit";;
  *             ) echo "Punctuation, whitespace, or other";;
esac      # محدوده کاراکترها در [براکت‌ها]، یا محدوده‌های
          #+POSIX‎ در ‎[[‎ براکت‌های دوتایی را اجازه می‌دهد.

#  در نگارش نخست این مثال، بررسی‌ها برای کاراکترهای حروف کوچک و بزرگ
#+    ‎[a-z]‎ و ‎[A-Z]‎ بود. این دیگر در برخی مناطق و/یا توزیع‌های لینوکس 
#                               کار نمی‌کند. ‎POSIX‎ بیشتر قابل حمل است.
#                با نشکر از ‎Frank Wang‎ برای اشاره نمودن به این مورد.

#                               :تمرین
#                              --------
#.چنانکه از اسکریپت معلوم است، ضربه کلید منفردی قبول کرده، خاتمه می‌یابد
#اسکریپت را طوری تغییر بدهید که ورودی‌های مکرر را دریافت کند، با هر ضربه
#+               کلید گزارش  بدهد، و فقط وقتی ‎"X"‎ زده می‌شود، خاتمه یابد.
#                     اشاره: همه چیز را درون یک حلقه ‎"while"‎ قرار بدهید.

exit 0

مثال ‎11-26‎. ایجاد منوها با استفاده از case

#!/bin/bash

#      بانک اطلاعاتی خام آدرس

clear # .پاک کردن صفحه نمایش

echo "          Contact List"
echo "          ------- ----"
echo "Choose one of the following persons:" 
echo
echo "[E]vans, Roland"
echo "[J]ones, Mildred"
echo "[S]mith, Julie"
echo "[Z]ane, Morris"
echo

read person

case "$person" in
# .توجه کنید متغیر نقل‌قولی می‌شود

  "E" | "e" )
  # .قبول حرف بزرگ یا کوچک ورودی
  echo
  echo "Roland Evans"
  echo "4321 Flash Dr."
  echo "Hardscrabble, CO 80753"
  echo "(303) 734-9874"
  echo "(303) 734-9892 fax"
  echo "revans@zzy.net"
  echo "Business partner & old friend"
  ;;
#.به سمی‌کالن دوتایی برای خاتمه دادن هر گزینه توجه کنید

  "J" | "j" )
  echo
  echo "Mildred Jones"
  echo "249 E. 7th St., Apt. 19"
  echo "New York, NY 10009"
  echo "(212) 533-2814"
  echo "(212) 533-9972 fax"
  echo "milliej@loisaida.com"
  echo "Ex-girlfriend"
  echo "Birthday: Feb. 11"
  ;;

# بعداً  اطلاعات ‎Smith و ‎Zane‎ را اضافه کنید.

          * )
   # گزینه قراردادی  
   # .ورودی تهی (زدن کلید اینتر) نیز در اینجا مطابقت می‌کند
   echo
   echo "Not yet in database."
  ;;

esac

echo

#                     :تمرین
#                    --------
#  اسکریپت را طوری تغییر بدهید که به جای خاتمه یافتن
#+ .پس از نمایش فقط یک آدرس، چندین ورودی را قبول کند

exit 0

یک استفاده هوشمندانه استثنایی case به بررسی پارامترهای سطر فرمان مربوط می‌شود.

#! /bin/bash

case "$1" in
  "") echo "Usage: ${0##*/} <filename>"; exit $E_PARAM;;
#       .اگر فاقد پارامتر سطر فرمان باشد، یا پارامتر اول تهی باشد
#  توجه کنید که ‎${0##*/}‎ جایگزینی پارامتر ‎${var##pattern}‎ است.
#                                          نتیجه خالص ‎$0‎ است.
  -*) FILENAME=./$1;;
# اگر نام فایل داده شده به عنوان ‎($1)‎ با یک خط تیره شروع شود، آن
#+  را با ‎./$1‎ تعویض کند طوری که فرمانها دیگر آن را به عنوان 
#+                                     .یک گزینه تفسیر نکنند

  * ) FILENAME=$1;;
#                                     در غیر اینصورت، ‎$1‎ باشد.
esac

این هم یک مثال قابل فهم‌تر در مورد مدیریت پارامتر خط فرمان:

#! /bin/bash


while [ $# -gt 0 ]; do               #         تا تمام کردن پارامترها
  case "$1" in
    -d|--debug)
                                     #  پارامتر ‎"-d"‎ یا ‎"--debug"‎ است؟
              DEBUG=1
              ;;
    -c|--conf)
              CONFFILE="$2"
              shift
              if [ ! -f $CONFFILE ]; then
                echo "Error: Supplied file doesn't exist!"
                exit $E_CONFFILE     #         .خطای پیدا نشدن فایل
              fi
              ;;
  esac
  shift                              #  .کنترل مجموعه بعدی پارامترها
done

    #   از اسکریپت "Log2Rot" بسته ‎"rottlog"‎ متعلق به ‎Stefano Falsetto‎
    #                  .که با مجوز از ایشان در اینجا استفاده شده است

مثال ‎11-27‎. کاربرد جایگزینی فرمان برای تولید متغیر case

#!/bin/bash
# ‎case-cmd.sh‎: استفاده از جایگزینی فرمان در تولید یک متغیر ‎"case"‎.

case $( arch ) in   # ‎$( arch )‎ معماری کامپیوتر را برگشت می‌دهد.
                    # معادل با ‎'uname -m'‎ 
  i386 ) echo "80386-based machine";;
  i486 ) echo "80486-based machine";;
  i586 ) echo "Pentium-based machine";;
  i686 ) echo "Pentium2+-based machine";;
  *    ) echo "Other type of machine";;
esac

exit 0

یک ساختار case می‌تواند رشته‌ها را بواسطه الگوهای globbing فیلتر کند.

مثال ‎11-28‎. انطباق رشته ساده

#!/bin/bash
# ‎match-string.sh‎: انطباق رشته ساده
#          کاربرد یک ساختار ‎'case'‎.

match_string ()
{ # Exact string match.
  MATCH=0
  E_NOMATCH=90
  PARAMS=2     # .تابع به ۲ شناسه نیاز دارد
  E_BAD_PARAMS=91

  [ $# -eq $PARAMS ] || return $E_BAD_PARAMS

  case "$1" in
  "$2") return $MATCH;;
  *   ) return $E_NOMATCH;;
  esac

}  


a=one
b=two
c=three
d=two


match_string $a     # تعداد پارامترهای نادرست
echo $?             # 91

match_string $a $b  # عدم انطباق
echo $?             # 90

match_string $b $d  # انطباق
echo $?             # 0


exit 0		    

مثال ‎11-29‎. کنترل برای ورودی الفبایی

#!/bin/bash
#        ‎isalpha.sh‎: کاربرد ساختار ‎"case"‎ برای فیلتر کردن یک رشته.

SUCCESS=0
FAILURE=1   #	   	        ‎FAILURE=-1‎ بود اما Bash دیگر برگشت
            #+     		  .دادن کمیت منفی را اجازه نمی‌دهد

isalpha ()  #.بررسی می‌کند آیا «کاراکتر اول» رشته ورودی الفبایی است
{
if [ -z "$1" ]          	     #  آیا شناسه‌ای ارایه نشده است؟
then
  return $FAILURE
fi

case "$1" in
  [a-zA-Z]*) return $SUCCESS;;        #      با یک حرف شروع می‌شود؟
  *        ) return $FAILURE;;
esac
}                #    این را با تابع ‎"isalpha ()"‎ در ‎C‎ مقایسه کنید.


isalpha2 ()      #         بررسی می‌کند آیا «تمام رشته» الفبایی است.
{
  [ $# -eq 1 ] || return $FAILURE

  case $1 in
  *[!a-zA-Z]*|"") return $FAILURE;;
               *) return $SUCCESS;;
  esac
}

isdigit ()       #           بررسی می کند آیا «تمام رشته» عددی است.
{                #       به بیان دیگر، برای متغیر صحیح بررسی می‌کند.
  [ $# -eq 1 ] || return $FAILURE

  case $1 in
    *[!0-9]*|"") return $FAILURE;;
              *) return $SUCCESS;;
  esac
}



check_var ()    #  وابسته به تابع ‎isalpha ()‎
{
if isalpha "$@"
then
  echo "\"$*\" begins with an alpha character."
  if isalpha2 "$@"
  then          #اگر کاراکتر اول الفبایی نباشد، این بررسی لزومی ندارد.
    echo "\"$*\" contains only alpha characters."
  else
    echo "\"$*\" contains at least one non-alpha character."
  fi  
else
  echo "\"$*\" begins with a non-alpha character."
                # همچنین اگر شناسه‌ای داده نشده باشد، غیر الفبایی است.
fi

echo

}

digit_check ()  #  وابسته به تابع ‎isdigit ()‎
{
if isdigit "$@"
then
  echo "\"$*\" contains only digits [0 - 9]."
else
  echo "\"$*\" has at least one non-digit character."
fi

echo

}

a=23skidoo
b=H3llo
c=-What?
d=What?
e=$(echo $b)    #  جایگزینی فرمان.
f=AbcDef
g=27234
h=27a34
i=27.34

check_var $a
check_var $b
check_var $c
check_var $d
check_var $e
check_var $f
check_var       #  شناسه‌ای داده نشده، بنابراین چه می‌شود؟
#
digit_check $g
digit_check $h
digit_check $i


exit 0          #  اسکریپت توسط ‎S.C.‎ بهینه شده است.

#                        :تمرین
#                       --------
# یک تابع ‎isfloat ()‎ بنویسید که اعداد اعشاری را بررسی کند.
#   اشاره: تابع رونوشت ‎isdigit ()‎ است اما یک تست برای بررسی 
#+                         نقطه اعشار اختیاری اضافه می‌شود.

select

ساختار select، که از پوسته ‎Korn‎ اقتباس گردیده است، ابزار دیگری برای ساختن منوها است.

selectvariable [in list]
do
command...
 break
done

این ساختار، برای وارد کردن یکی از انتخاب‌های نمایش داده شده در ‎variable list اعلانی به کاربر ارایه می‌دهد‎. توجه کنید که select به طور پیش‌فرض اعلان ‎$PS3‎ ‏(‎#?‎) را به کار می‌برد، اما این اعلان می‌تواند تغییر داده شود.

مثال ‎11-30‎. تولید منوها با استفاده از select

#!/bin/bash

PS3='Choose your favorite vegetable: ' #          .تنظیم رشته اعلان
                                       #   در غیر اینصورت پیش‌فرض ‎#?‎

echo

select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
do
  echo
  echo "Your favorite veggie is $vegetable."
  echo "Yuck!"
  echo
  break  #  اگر در اینجا ‎'break'‎ نباشد چه اتفاقی رخ می‌دهد؟
done

exit

#                        :تمرین
#                       --------
#  این اسکریپت را برای عکس‌العمل در برابر ورودی کاربر
#+             تعیین نشده در جمله ‎"select"‎ اصلاح کنید.
#  برای مثال، اگر کاربر ‎"peas"‎ را وارد نماید، اسکریپت
#+        پاسخ بدهد ‎"Sorry. That is not on the menu."‎

اگر ‎in list‎ ذکر نگردد، آنوقت select لیست شناسه‌های سطر فرمان ‎($@)‎ عبور داده شده به اسکریپت یا تابع محتوی ساختار select را به کار می‌برد.

این مورد را با رفتار ساختار ‎for variable [in list]‎ که ‎in list‎ در آن از قلم افتاده، مقایسه کنید.

مثال ‎11-31‎. تولید منوها با استفاده از select در یک تابع

#!/bin/bash

PS3='Choose your favorite vegetable: '

echo

choice_of()
{
select vegetable
# ‎[in list]‎ ذکر نشده، پس ‎'select'‎ شناسه‌های داده شده به تابع را استفاده می‌کند.
do
  echo
  echo "Your favorite veggie is $vegetable."
  echo "Yuck!"
  echo
  break
done
}

choice_of beans rice carrots radishes rutabaga spinach
#         $1    $2   $3      $4       $5       $6
#	             تحویل داده شده به تابع ‎choice_of()‎

exit 0

همچنین مثال ‎37-3‎ را ببینید.

یادداشت‌ها

[1]

برای چیدمانی با ظاهر شکیل‌تر، سطرهای انطباق الگو می‌توانند با یک پرانتز سمت چپ -‎(‎ - نیز شروع بشوند.

case $( arch ) in   # ‎$( arch )‎ معماری ماشین را برگشت می‌دهد.
  ( i386 ) echo "80386-based machine";;
# ^      ^
  ( i486 ) echo "80486-based machine";;
  ( i586 ) echo "Pentium-based machine";;
  ( i686 ) echo "Pentium2+-based machine";;
  (    * ) echo "Other type of machine";;
esac