فصل ‎26‎- ساختارهای لیست

ساختارهای ‎and list‎ و ‎or list‎ وسیله‌ای برای پردازش تعدادی فرمان متوالی فراهم می‌نمایند. اینها می‌توانند به طور کارآمدی جایگزین ‎if/then‎ پیچیده تو در تو یا حتی دستورالعمل‌های case بشوند.

زنجیر کردن فرمانها با یکدیگر

and list

command-1 && command-2 && command-3 && ... command-n
هر فرمان به نوبت اجرا می‌شود، به شرط آنکه فرمان قبل از آن دارای یک مقدار برگشتی true (صفر) باشد. با اولین false (غیر صفر) برگشتی، زنجیره فرمان خاتمه داده می‌شود (اولین فرمان برگشت دهنده false، آخرین فرمان اجرا شده است).

یک مورد استفاده جالب از یک ‎and list‎ دو شرطی، از نگارش قدیمی اسکریپت بازی Tetris نوشته YongYe:

equation()
{     #الگوریتم مرکزی به کار رفته برای دوبرابر یا نصف کردن مختصات
   [[ ${cdx} ]] && ((y=cy+(ccy-cdy)${2}2))
   eval ${1}+=\"${x} ${y} \"
}

مثال ‎26-1‎. کاربرد یک ‎and list‎ برای تست کردن شناسه‌های خط فرمان

#!/bin/bash
# and list

if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && \
#                ^^                         ^^               ^^
echo "Argument #2 = $2"
then
  echo "At least 2 arguments passed to script."
  #        تمام فرمان‌های زنجیر شده، ‎true‎ برگشت می‌دهند.
else
  echo "Fewer than 2 arguments passed to script."
  #حداقل یکی از فرمان‌های زنجیر شده، false برگشت می‌دهد.
fi  
#   توجه کنید که ‎if [ ! -z $1 ]‎ کار می‌کند، اما مورد به
#             اصطلاح معادل آن، ‎if [ -n $1 ]‎ کار نمی‌کند.
#             اگر چه، نقل‌قولی کردن، آن را تعمیر می‌کند.
#                         ‎if "[ -n "$1" ]"‎  کار می‌کند.
#                             مواظب باشید!     ^  ^ 
#   همیشه بهتر است متغیرهایی که باید رسیدگی بشوند، نقل‌قول گردند.

#این همان کار را با استفاده از جمله‌های ‎if/then‎ خالص انجام می‌دهد.
if [ ! -z "$1" ]
then
  echo "Argument #1 = $1"
fi
if [ ! -z "$2" ]
then
  echo "Argument #2 = $2"
  echo "At least 2 arguments passed to script."
else
  echo "Fewer than 2 arguments passed to script."
fi
#                   طولانی‌تر و کندتر از کاربرد یک «‎and list‎» است.

exit $?

مثال ‎26-2‎. یک شناسه خط فرمان دیگر با استفاده از یک ‎and list‎

#!/bin/bash

ARGS=1        #               تعداد شناسه‌های مورد انتظار.
E_BADARGS=85  #مقدار برگشتی اگر تعداد شناسه‌ها صحیح نباشد.

test $# -ne $ARGS && \
#    ^^^^^^^^^^^^          شرط شماره ‎1‎
echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS
#                                             ^^
#اگر شرط شماره ‎1‎ صحیح باشد (تعداد شناسه‌های اسکریپت نادرست باشد)
#+           آنوقت بقیه سطر اجرا می‌شود، و اسکریپت خاتمه می‌یابد.

#    سطر زیر فقط در صورتی اجرا می‌گردد که بررسی فوق ناموفق باشد.
echo "Correct number of arguments passed to this script."

exit 0

#برای کنترل مقدار برگشتی، بعد از خاتمه یافتن اسکریپت یک ‎echo $?‎ انجام بدهید.

البته، یک ‎and list‎ همچنین می‌تواند متغیرها را به یک مقدار پیش‌فرض تنظیم نماید.

arg1=$@ && [ -z "$arg1" ] && arg1=DEFAULT
		
    # اگر شناسه‌(های) خط فرمان موجود باشند، ‎arg1‎ را به آنها تنظیم می‌کند. اما
    # اگر در خط فرمان شناسه‌ای تعیین نشده باشد آن را به ‎DEFAULT‎ تنظیم می‌کند.

or list

command-1 || command-2 || command-3 || ... command-n
هر فرمان به شرط آن که فرمان قبل از آن «false» را برگشت بدهد به نوبت اجرا می‌شود. با اولین «true» برگشتی، زنجیره فرمان خاتمه می‌یابد (اولین فرمان برگشت دهنده «true»، آخرین فرمان اجرا شده است). به طور آشکاری برعکس «‎and list‎» است.

مثال ‎26-3‎. به کار بردن ‎or list‎ها در ترکیب با یک ‎and list‎

#!/bin/bash
#  delete.sh :یک برنامه حذف نه چندان ماهرانه.
#  طرز کاربرد: ‎delete filename‎

E_BADARGS=85

if [ -z "$1" ] 
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS  #بدون شناسه است؟ خروج.
else  
  file=$1          #      تنظیم نام فایل.
fi  

[ ! -f "$file" ] && echo "File \"$file\" not found. \
Cowardly refusing to delete a nonexistent file."
#  ‎AND LIST‎، برای ارایه پیغام خطا در صورت موجود نبودن فایل.
#  توجه، پیغام ‎echo‎ بعد از یک \ در سطر دوم ادامه یافته است.

[ ! -f "$file" ] || (rm -f $file; echo "File \"$file\" deleted.")
#                 ‎OR LIST‎ برای حذف فایل در صورت موجود بودن.

#                           به وارونگی منطقِ فوق توجه کنید.
#‎AND LIST‎ بر ‎true‎ اجرا می‌شود، ‎OR LIST‎ بر ‎false‎ اجرا می‌گردد.
exit $?

Caution

اگر فرمان نخست در یک ‎or list‎ «صحیح» را برگشت بدهد، آن فرمان اجرا خواهد گردید.

# ==>        برش‌های زیر از اسکریپت ‎/etc/rc.d/init.d/single‎
#+==>       نوشته ‎Miquel van Smoorenburg‎ است که استفاده از
#+==>                لیست‌های «and» و «or» را توضیح می‌دهند.
# ==> توضیحات جهت گذاری شده، توسط نگارنده اضافه گردیده‌اند.

[ -x /usr/bin/clear ] && /usr/bin/clear
  # ==>اگر ‎/usr/bin/clear‎ وجود دارد، آنوقت فراخوانی بشود.
  # ==>  کنترل وجود یک فرمان قبل از فراخوانی آن فرمان، از
  #+==>پیغام‌های خطا و سایر پیامدهای ناپسند پیش‌گیری می‌کند.

  # ==> . . .
# اگر بخواهند چیزی را در وضعیت کاربر منفرد اجرا نمایند، می‌توانند این کار را انجام بدهند...
for i in /etc/rc1.d/S[0-9][0-9]* ; do
        #              کنترل موجود بودن اسکریپت.
        [ -x "$i" ] || continue
  # ==>اگر فایل متناظر در ‎$PWD‎ پیدا نشود، آنوقت
  #+==> با پرش به ابتدای حلقه «ادامه» داده شود.

        # رد کردن فایل‌های پشتیبان و فایل‌های تولید شده به وسیله ‎rpm‎
        case "$1" in
                *.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
                        continue;;
        esac
        [ "$i" = "/etc/rc1.d/S00single" ] && continue
  # ==>نام اسکریپت تنظیم می‌گردد، اما هنوز اجرا نمی‌شود.
        $i start
done
  # ==> . . .

important وضعیت خروج یک ‎and list‎ یا یک ‎or list‎، وضعیت خروج آخرین فرمان اجرا شده است.

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

false && true || echo false         # false

#          همان نتیجه در اثر
( false && true ) || echo false     # false
#             اما نه در مورد
false && ( true || echo false )     #    (چیزی منعکس نمی‌شود)

#به گروه‌بندی چپ به راست و دستورالعمل‌های ارزیابی توجه نمایید.

#      به طور معمول بهتر است از چنین پیچیدگی‌هایی پرهیز گردد.
# با تشکر از ‎S.C.‎

برای نمایش تجربی کاربرد ساختارهای ‎and/or list‎ جهت تست کردن متغیرها، مثال ‎A-7‎ و مثال ‎7-4‎ را مشاهده کنید.