فصل 36- گوناگون

‎36.7‎- نکته‌های گوناگون

‎36.7.1‎- ایده‌هایی برای اسکریپت‌های قدرتمندتر

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

  • #!/bin/bash
    
    ARGCOUNT=1            # نام به عنوان شناسه لازم است.
    E_WRONGARGS=65
    
    if [ number-of-arguments is-not-equal-to "$ARGCOUNT" ]
    #    ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
    #    نمی‌توانید چگونه نوشتن کد آنرا تعیین کنید . . .
    #+          . . . بنابراین به صورت شبه کد می‌نویسید.
    
    then
      echo "Usage: name-of-script name"
      #            ^^^^^^^^^^^^^^   مقدار دیگری شبه کد.
      exit $E_WRONGARGS
    fi 
    
    . . .
    
    exit 0
    
    
    # بعداً، کدی که کار می‌کند را با شبه کد جایگزین کنید.
    
    # سطر ‎6‎ می‌شود:
    if [ $# -ne "$ARGCOUNT" ]
    
    # سطر ‎12‎ می‌شود:
      echo "Usage: `basename $0` name"

    برای یک مثال استفاده از شبه کد، تمرین Square Root را ببینید.

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

  • #سطرها را در انتهای هر اسکریپتی که باید پیگردی شود درج ‎(>>)‎ کنید.
    
    whoami>> $SAVE_FILE                #   کاربر احضار کننده اسکریپت.
    echo $0>> $SAVE_FILE               #                 نام اسکریپت.
    date>> $SAVE_FILE                  #                زمان و تاریخ.
    echo>> $SAVE_FILE                  # سطر خالی به عنوان جدا کننده.
    
    #   البته، باید SAVE_FILE به عنوان یک متغیر محیط در فایل‎~/.bashrc‎
    #+         تعریف و export گردیده باشد (چیزی مانند ‎~/.scripts-run‎)

  • عملگر ‎>>‎ سطرها را به یک فایل پیوست می‌کند. اگر بخواهید سطری را به یک فایل موجود prepend نمایید، یعنی در ابتدای آن اضافه کنید چطور؟

  • file=data.txt
    title="***This is the title line of data text file***"
    
    echo $title | cat - $file >$file.new
    #‎"cat -"‎ ورودی استاندارد را به ‎$file‎ الحاق می‌نماید.
    #   نتیجه نهایی عبارت است از: نوشتن یک فایل جدید با
    #+                 ‎$title‎ اضافه شده در «ابتدای» آن.

    این نمونه ساده‌ شده از اسکریپت مثال ‎19-13‎ است که قبلاً ارایه گردیده. و البته، sed نیز می‌تواند این کار را انجام بدهد.

  • یک اسکریپت پوسته می‌تواند به عنوان فرمان جاسازی شده در درون یک اسکریپت پوسته دیگر، یک اسکریپت Tcl یا wish، یا حتی یک Makefile عمل نماید. اسکریپت می‌تواند به عنوان یک فرمان خارجی پوسته در یک برنامه C با استفاده از فراخوان ‎system()‎ به عنوان مثال، به صورت ‎system("script_name");‎ فراخوانی بشود.

  • تنظیم یک متغیر به محتویات یک اسکریپت جاسازی شده sed یا awk خوانایی wrapper پوسته احاطه کننده را افزایش می‌دهد. مثال ‎A-1‎ و مثال ‎15-20‎ را ملاحظه نمایید.

  • فایل‌های شامل سودمندترین تعریف‌ها و توابع برگزیده‌تان را بهم پیوسته کنید. در صورت لزوم، یک یا چند مورد از این «فایل‌های کتابخانه» را با استفاده از فرمان dot ‎(.)‎ یا source در اسکریپت‌ها «include» ‏(پیوست) کنید.

  • # SCRIPT LIBRARY
    # ------ -------
    
    #   توجه:
    # در اینجا نه ‎"#!"‎ و نه 
    #    کد موثر قرار ندارد.
    
    
    # تعریف‌های مفید متغیر
    
    ROOT_UID=0             #        Root دارای ‎$UID‎ برابر ‎0‎ است.
    E_NOTROOT=101          #              حطای کاربر root نبودن. 
    MAXRETVAL=255          # حداکثر (مثبت) مقدار برگشتی یک تابع.
    SUCCESS=0
    FAILURE=-1
    
    
    
    # توابع
    
    Usage ()               #   پیغام «‎Usage:‎ ».
    {
      if [ -z "$1" ]       # شناسه‌ای داده نشده.
      then
        msg=filename
      else
        msg=$@
      fi
    
      echo "Usage: `basename $0` "$msg""
    }  
    
    
    Check_if_root ()       #  کنترل اجرای اسکریپت توسط root.
    {                      #         از اسکریپت مثال ‎ex39.sh‎
      if [ "$UID" -ne "$ROOT_UID" ]
      then
        echo "Must be root to run this script."
        exit $E_NOTROOT
      fi
    }  
    
    
    CreateTempfileName ()  # یک نام فایل انحصاری ایجاد می‌کند.
    {                      #          از اسکریپت مثال ‎ex51.sh‎
      prefix=temp
      suffix=`eval date +%s`
      Tempfilename=$prefix.$suffix
    }
    
    
    isalpha2 ()            #  بررسی آنکه «تمام اسکریپت» الفبایی است.
    {                      #              از اسکریپت مثال ‎isalpha.sh‎
      [ $# -eq 1 ] || return $FAILURE
    
      case $1 in
      *[!a-zA-Z]*|"") return $FAILURE;;
      *) return $SUCCESS;;
      esac                 #                         با تشکر از ‎S.C.‎
    }
    
    
    abs ()                           #         کمیت خالص (قدر مطلق)‏.
    {                                #توجه:حداکثر مقدار برگشتی= 255.
      E_ARGERR=-999999
    
      if [ -z "$1" ]                 #      عبور دادن شناسه لازم است.
      then
        return $E_ARGERR             #      مقدار برگشتی خطای مشهود.
      fi
    
      if [ "$1" -ge 0 ]              #  اگر منفی نیست،
      then                           #
        absval=$1                    # همانطور می‌ماند.
      else                           # در غیر آن صورت،
        let "absval = (( 0 - $1 ))"  #     تعویض علامت.
      fi  
    
      return $absval
    }
    
    
    tolower ()             # رشته(های) عبور داده شده به عنوان شناسه
    {                      #+          را به حروف کوچک تبدیل می‌کند.
    
      if [ -z "$1" ]       #                     اگر شناسه‌ای نباشد،
      then                 
        echo "(null)"      #+   فرستادن پیغام خطا و بازگشت از تابع.
        return             
      fi  
    
      echo "$@" | tr A-Z a-z
      #      برگردان تمام شناسه‌های عبور داده شده ‎($@)‎‏ به حروف کوچک.
    
      return
    
    #برای تنظیم متغیر به خروجی تابع از جایگزینی فرمان استفاده کنید.
    #                    برای مثال:
    #    oldvar="A seT of miXed-caSe LEtTerS"
    #    newvar=`tolower "$oldvar"`
    #    echo "$newvar"           # مجموعه‌ای از حروف با حالت مختلط.
    #
    #تمرین: این تابع را برای تبدیل شناسه‌(های) داده شده با حروف کوچک
    #             به حروف بزرگ بازنویسی کنید ... ‎toupper()‎  ‏[‎آسان‎]‏.
    }

  • برای افزایش وضوح و خوانایی از سر صفحه‌های توضیح خاص منظوره در اسکریپت‌ها استفاده کنید.

  • ## اخطار.
    rm -rf *.zzy   ## گزینه‌های ‎-rf‎ برای rm بسیار خطرناک هستند،
                   ##+  مخصوصاً همراه کاراکترهای جایگزین شونده.
    
    #+ ادامه سطر.
    # این سطر ‎1‎ از یک توضیح
    #+      چند سطری است، و
    #+ این سطر انتهایی است.
    
    #* نکته.
    
    #o اقلام لیست.
    
    #> از یک نقطه نظر دیگر.
    while [ "$var1" != "end" ]    #> while test "$var1" != "end"

  • ‎Dotan Barak‎ کد الگو برای یک نوار پیشرفت در اسکریپت را ارایه می‌کند.

  • مثال ‎36-17‎. یک نوار پیشرفت

    #!/bin/bash
    # progress-bar.sh
    
    # نویسنده: ‎Dotan Barak‎ (با تجدید نظر جزیی به وسیله نگارنده).
    #               با مجوز در این راهنما استفاده گردیده(تشکر!)‏.
    
    
    BAR_WIDTH=50
    BAR_CHAR_START="["
    BAR_CHAR_END="]"
    BAR_CHAR_EMPTY="."
    BAR_CHAR_FULL="="
    BRACKET_CHARS=2
    LIMIT=100
    
    print_progress_bar()
    {
            #   محاسبه تعداد کاراکترهایی که پر خواهد بود.
            let "full_limit = ((($1 - $BRACKET_CHARS) * $2) / $LIMIT)"
    
            # محاسبه تعداد کاراکترهایی که خالی خواهد بود.
            let "empty_limit = ($1 - $BRACKET_CHARS) - ${full_limit}"
    
            #                            آماده کردن نوار.
            bar_line="${BAR_CHAR_START}"
            for ((j=0; j<full_limit; j++)); do
                    bar_line="${bar_line}${BAR_CHAR_FULL}"
            done
    
            for ((j=0; j<empty_limit; j++)); do
                    bar_line="${bar_line}${BAR_CHAR_EMPTY}"
            done
    
            bar_line="${bar_line}${BAR_CHAR_END}"
    
            printf "%3d%% %s" $2 ${bar_line}
    }
    
    #   این هم نمونه کدی که آن را به کار می‌برد.
    MAX_PERCENT=100
    for ((i=0; i<=MAX_PERCENT; i++)); do
            #
            usleep 10000
            # ...یا اجرای برخی فرمان‌های دیگر...
            #
            print_progress_bar ${BAR_WIDTH} ${i}
            echo -en "\r"
    done
    
    echo ""
    
    exit
  • یک استفاده به ویژه ماهرانه از ساختارهای ‎if-test‎ مربوط به بلوک‌های توضیح است.

  • #!/bin/bash
    
    COMMENT_BLOCK=
    # برای یک غافلگیری ناخوشایند،
    #+  به متغیر فوق مقدار بدهید.
    
    if [ $COMMENT_BLOCK ]; then
    
    Comment block --
    =================================
    This is a comment line.
    This is another comment line.
    This is yet another comment line.
    =================================
    
    echo "This will not echo."
    
    Comment blocks are error-free! Whee!
    
    fi
    
    echo "No more comments, please."
    
    exit 0

    این مورد را با کاربرد ‎here document‎ها برای توضیح گذاری بلوک‌های کد مقایسه کنید.

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

  • #!/bin/bash
    
    SUCCESS=0
    E_BADINPUT=85
    
    test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null
    # یک عدد صحیح یا مساوی ‎0‎ هست یا مساوی ‎0‎ نیست.
    #       ‎2>/dev/null‎ پیغام خطا را خاموش می‌کند.
    
    if [ $? -ne "$SUCCESS" ]
    then
      echo "Usage: `basename $0` integer-input"
      exit $E_BADINPUT
    fi
    
    let "sum = $1 + 25"          # اگر ‎$1‎ صحیح نباشد پیغام خطا خواهد داد.
    echo "Sum = $sum"
    
    # هر متغیری، نه فقط یک پارامتر خط فرمان، می‌تواند به این روش تست بشود.
    
    exit 0

  • محدوده ‎0 - 255‎ برای مقادیر برگشتی توابع، یک محدودیت سخت‌گیرانه است. متغیرهای سراسری و سایر راه‌کارهای موقتی بیشتر اوقات مشکل‌آفرین هستند. یک روش دیگر برای انتقال دادن مقدار برگشتی از تابع به بدنه اصلی اسکریپت، داشتن تابعی برای نوشتن «مقدار برگشتی» در stdout (معمولاً با echo) و تخصیص آن به یک متغیر است. این روش در حقیقت نوعی از جایگزینی فرمان است.

  • مثال ‎36-18‎. برگشت مقدار به طور ترفندگونه

    #!/bin/bash
    # multiplication.sh
    
    multiply ()                     # پارامترهای داده شده را ضرب می‌کند.
    {                               # تعداد شناسه‌های متغیری قبول می‌کند.
    
      local product=1
    
      until [ -z "$1" ]             #     تا تمام شدن شناسه‌های داده شده
      do
        let "product *= $1"
        shift
      done
    
      echo $product                 #  در خروجی استاندارد منعکس نمی‌شود،
    }                               #+   چون به متغیر تخصیص خواهد یافت.
    
    mult1=15383; mult2=25211
    val1=`multiply $mult1 $mult2`
    # stdout تابع یعنی خروجی echo در تابع را به متغیر val1 تخصیص می‌دهد.
    echo "$mult1 X $mult2 = $val1"                          # 387820813
    
    mult1=25; mult2=5; mult3=20
    val2=`multiply $mult1 $mult2 $mult3`
    echo "$mult1 X $mult2 X $mult3 = $val2"                 #      2500
    
    mult1=188; mult2=37; mult3=25; mult4=47
    val3=`multiply $mult1 $mult2 $mult3 $mult4`
    echo "$mult1 X $mult2 X $mult3 X $mult4 = $val3"        #   8173300
    
    exit 0

    همین تکنیک برای رشته‌های الفبایی نیز کار می‌کند. این بدان معنی است که یک تابع می‌تواند یک مقدار غیر عددی «برگشت» بدهد.

    capitalize_ichar ()          # کاراکتر ابتدایی شناسه(های) رشته‌ای عبور
    {                            #+  داده شده را به حرف بزرگ نبدیل می‌کند.
    
      string0="$@"               #        شناسه‌های چندتایی را قبول می‌کند.
    
      firstchar=${string0:0:1}   #                           کاراکتر اول.
      string1=${string0:1}       #                         بقیه رشته(ها).
    
      FirstChar=`echo "$firstchar" | tr a-z A-Z`
                                 #         تبدیل کاراکتر اول به حرف بزرگ.
    
      echo "$FirstChar$string1"  #         بیرون دادن در خروجی استاندارد.
    
    }  
    
    newstring=`capitalize_ichar "every sentence should start with a capital letter."`
    echo "$newstring"  #Every sentence should start with a capital letter.

    با این شیوه حتی «برگشت» دادن چندین کمیت نیز برای یک تابع امکان‌پذیر است.

    مثال ‎36-19‎. برگشت دادن ترفندگونه چند کمیت

    #!/bin/bash
    # sum-product.sh
    #                     تابع می‌تواند بیش از یک مقدار «برگشت» بدهد.
    
    sum_and_product ()   # محاسبه مجموع و حاصلضرب شناسه‌های داده شده.
    {
      echo $(( $1 + $2 )) $(( $1 * $2 ))
    # کمیت‌های محاسبه شده را با فاصله در خروجی استاندارد منعکس می‌کند.
    }
    
    echo
    echo "Enter first number "
    read first
    
    echo
    echo "Enter second number "
    read second
    echo
    
    retval=`sum_and_product $first $second`      # خروجی تابع را تخصیص می‌دهد.
    sum=`echo "$retval" | awk '{print $1}'`      #  فیلد نخست را تخصیص می‌دهد.
    product=`echo "$retval" | awk '{print $2}'`  #   فیلد دوم را تخصیص می‌دهد.
    
    echo "$first + $second = $sum"
    echo "$first * $second = $product"
    echo
    
    exit 0

    Caution

    برای انجام این کار فقط یک عبارت echo می‌تواند در تابع وجود داشته باشد. اگر مثال قبل را تغییر بدهید:

    sum_and_product ()
    {
      echo "This is the sum_and_product function." 
      echo $(( $1 + $2 )) $(( $1 * $2 ))
    }
    ...
    retval=`sum_and_product $first $second`   #خروجی تابع را تخصیص می‌دهد.
    # اکنون، این به طور صحیح عمل نخواهد نمود.

  • مورد بعدی در کیسه حقه‌هایمان، تکنیک‌هایی برای عبور دادن یک آرایه به یک تابع، سپس «برگشت دادن» یک آرایه به بدنه اصلی اسکریپت است.

    عبور دادن یک آرایه مستلزم بارگذاری عناصر آرایه به صورت جدا شده با فاصله به درون یک متغیر به وسیله استفاده از جایگزینی فرمان است. برای دوباره به دست آوردن یک آرایه به عنوان «مقدار برگشتی» از تابع، راهبرد ذکر شده قبلی، یعنی echo نمودن آرایه در تابع، و سپس فراخوانی جایگزینی فرمان و عملگر ‎( ... )‎ برای تخصیص آن به یک آرایه استفاده می‌شود.

  • مثال ‎36-20‎. عبور دادن و برگشت دادن آرایه‌ها

    #!/bin/bash
    
    # array-function.sh:     عبور دادن یک آرایه به یک تابع و 
                           #  برگشت دادن یک آرایه از یک تابع
    
    Pass_Array ()
    {
      local passed_array   # متغیر محلی!
      passed_array=( `echo "$1"` )
      echo "${passed_array[@]}"
      #  لیست کردن تمام عناصر آرایه جدید
      #+ تعریف و تنظیم شده در داخل تابع.
    }
    
    original_array=( element1 element2 element3 element4 element5 )
    
    echo
    echo "original_array = ${original_array[@]}"
    # لیست کردن تمام عناصر آرایه اصلی.
    
    #         این شگردی است که عبور دادن یک آرایه به یک تابع را میسر می‌کند.
    # *********************************************************************
    argument=`echo ${original_array[@]}`
    # *********************************************************************
    # قرار دادن تمام عناصر آرایه اصلی به صورت جدا شده با فاصله در یک متغیر.
    #
    #                      تلاش برای فقط عبور دادن خود آرایه کار نخواهد کرد.
    
    # این شگردی است که تصرف یک آرایه به عنوان «مقدار برگشتی» را میسر می‌کند.
    # *********************************************************************
    returned_array=( `Pass_Array "$argument"` )
    # *********************************************************************
    #                        تخصیص خروجی echo شده تابع به یک متغیر آرایه‌ای.
    
    echo "returned_array = ${returned_array[@]}"
    
    echo "============================================================="
    
    #  اکنون، دوباره امتحان کنید، تلاش برای دسترسی (لیست) آرایه از خارج تابع
    Pass_Array "$argument"
    
    #                 تابع خودش آرایه را لیست می‌کند، اما ...
    #+                 دسترسی آرایه از بیرون تابع ممنوع است.
    echo "Passed array (within function) = ${passed_array[@]}"
    #      مقدار تهی، چون آرایه یک متغیر محلی برای تابع است.
    
    echo
    
    #########################################################
    
    #                           و این هم یک مثال حتی واضح‌تر:
    
    ret_array ()
    {
      for element in {11..20}
      do
        echo "$element "   #  echo کردن عناصر جداگانه‌ای که در
      done                 #+داخل یک آرایه گردآوری خواهد شد.
    }
    
    arr=( $(ret_array) )   #        مجتمع کردن در داخل آرایه.
    
    echo "Capturing array \"arr\" from function ret_array () ..."
    echo "Third element of array \"arr\" is ${arr[2]}."  #13 (شاخص از صفر)
    echo -n "Entire array is: "
    echo ${arr[@]}                     #11 12 13 14 15 16 17 18 19 20
    
    echo
    
    exit 0
    
    # ‎Nathan Coulter‎ اشاره می‌کند که عبور دادن آرایه‌ها با عناصر
    #+       دارای فضای سفید، این مثال را با شکست مواجه می‌کند.

    برای یک مثال دارای جزییات بیشتر در مورد عبور دادن آرایه‌ها به توابع، مثال ‎A-10‎ را ببینید.

  • با استفاده از ساختار پرانتزهای دوگانه، به کار بردن گرامر سبک C جهت برقراری و کاهش و افزایش متغیرها و کاربرد این سبک در حلقه‌های for و while میسر می‌گردد. مثال ‎11-13‎ و مثال ‎11-18‎ را ببینید.

  • تنظیم path و umask در ابتدای یک اسکریپت آن را بیشتر قابل حمل می‌سازد -- برای اجرا بر روی یک ماشین «خارجی» که به احتمال بسیار ممکن است کاربر ‎$PATH‎ و umask را بهم ریخته باشد.

  • #!/bin/bash
    PATH=/bin:/usr/bin:/usr/local/bin ; export PATH
    umask 022   # فایل‌هایی که اسکریپت ایجاد می‌کند دارای مجوز ‎755‎ خواهند شد.
    
    # با تشکر از ‎Ian D. Allen‎ برای این نکته.

  • یک تکنیک اسکریپت‌نویسی سودمند، تغذیه تکراری خروجی یک فیلتر (به وسیله لوله‌کشی) به همان فیلتر، اما با مجموعه متفاوتی از شناسه‌ها و/یا گزینه‌ها می‌باشد. مخصوصاً tr و grep مناسب این کار هستند.

  • # از مثال ‎wstrings.sh‎.
    
    wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
    tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`

    مثال ‎36-21‎. بازی با مقلوبی‌ها

    #!/bin/bash
    # agram.sh: بازی کردن با مقلوبی‌ها.
    
    # پیدا کردن مقلوبی‌های...
    LETTERSET=etaoinshrdlu
    FILTER='.......'       # حداقل تعداد حروف
    #             1234567
    
    anagram "$LETTERSET" | # پیدا کردن تمام مقلوبی‌های letterset...
    grep "$FILTER" |       #              دارای حداقل ۷ حرف باشند 
    grep '^is' |           #                و با 'is' شروع می‌شوند،
    grep -v 's$' |         #                            جمع نباشند
    grep -v 'ed$'          #               افعال زمان گذشته نباشند
    #   افزودن ترکیب‌های بسیاری از شرط‌ها و فیلتر ها امکان پذیر است.
    
    #        از برنامه سودمند «anagram» که بخشی از بسته فهرست کلمه
    #+                    «yawl» نوشته نگارنده است، استفاده می‌کند.
    #  http://ibiblio.org/pub/Linux/libs/yawl-0.3.2.tar.gz
    #  http://bash.deta.in/yawl-0.3.2.tar.gz
    
    exit 0                 # انتهای کد.
    
    
    bash$ sh agram.sh
    islander
    isolate
    isolead
    isotheral
    
    
    
    #                                     تمرین‌ها:
    #                              --------------------
    # این اسکریپت را برای گرفتن LETTERSET به عنوان یک شناسه خط فرمان، ویرایش کنید.
    # فیلترها در سطرهای ۱۱ تا ۱۳ را پارامتری کنید (همچون ‎$FILTER‎)، به طوری که آنها
    #+                        بتوانند با عبور دادن شناسه‌ها به یک تابع تعیین بشوند.
    
    #   برای یک راهکار اندکی متفاوت در ایجاد مقلوبی‌ها اسکریپت ‎agram2.sh‎ را ببینید.
    

    همچنین مثال ‎29-4‎، مثال ‎16-25‎، و مثال ‎A-9‎ را ببینید.

  • از «‎here document‎ های بی نام» برای توضیح گذاری بلوک‌های کد استفاده کنید، تا در توضیح گذاری جداگانه هر سطر با یک # صرفه‌جوی کنید. مثال ‎19-11‎ را ببینید.

  • اجرای اسکریپت روی یک ماشین، که به فرمانی استناد نماید که شاید در آن ماشین نصب نشده باشد، خطرناک است. برای پرهیز از مشکلات بالقوه این مورد، از whatisاستفاده نمایید.

  • CMD=command1                      #         انتخاب اول.
    PlanB=command2                    #     گزینه عقب‌نشینی.
    
    command_test=$(whatis "$CMD" | grep 'nothing appropriate')
    #        اگر command1 روی سیستم پیدا نشود، whatis پیغام
    #+        ‎command1: nothing appropriate.‎ را برگشت می‌دهد
    #
    #                      یک جایگزین مطمئن‌تر عبارت است از:
    #    command_test=$(whereis "$CMD" | grep \/)
    #        اما آنوقت مفهوم تست پایین باید برعکس بشود، چون
    #+         در صورتی که ‎$CMD‎ روی سیستم موجود باشد، متغیر
    #+         ‎$command_testthe‎ فقط محتوا را نگهداری می‌کند.
    #                                  (با تشکر از bojster)
    
    
    if [[ -z "$command_test" ]]  #        کنترل وجود فرمان.
    then
      $CMD option1 option2       #اجرای command1 باگزینه‌ها.
    else                         #           در غیر آن صورت
      $PlanB                     #+          اجرای command2
    fi

  • یک ‎if-grep test‎ در یک حالت خطا، وقتی متن به جای stdout در stderr خارج می‌شود، ممکن است نتایج مورد انتظار را برگشت ندهد.

  • if ls -l nonexistent_filename | grep -q 'No such file or directory'
      then echo "File \"nonexistent_filename\" does not exist."
    fi

    تغییر مسیر دادن stderr به stdout این مشکل را برطرف می‌کند.

    if ls -l nonexistent_filename 2>&1 | grep -q 'No such file or directory'
    #                             ^^^^
      then echo "File \"nonexistent_filename\" does not exist."
    fi
    
    # با تشکر از ‎Chris Martin‎ برای توضیح دادن این مورد.

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

  • TMPFILE=tmpfile        # ایجاد یک فایل موقت برای نگهداری متغیر.
    
    (                                      #    داخل پوسته فرعی ...
    inner_variable=Inner
    echo $inner_variable
    echo $inner_variable >>$TMPFILE        #   افزودن به فایل موقت.
    )
    
                                           # خارج از پوسته فرعی ...
    
    echo; echo "-----"; echo
    echo $inner_variable                   #     تهی، مطابق انتظار.
    echo "-----"; echo
    
                                           #              اکنون ...
    read inner_variable <$TMPFILE          # باز خوانی متغیر پوسته.
    rm -f "$TMPFILE"                       #    حذف کردن فایل موقت.
    echo "$inner_variable" # سرهم بندی بد ریختی است، اما کار می‌کند.

  • فرمان run-parts برای اجرای یک مجموعه از اسکریپت‌ها با یک ترتیب خاص، مخصوصا در ترکیب با cron یا at سودمند است.

  • برای انجام تجدید نظرهای چندگانه روی اسکریپت‌های پیچیده، از بسته سیستم کنترل بازبینیrcs استفاده نمایید.

    از جمله سایر مزیت‌های این بسته، به هنگام‌سازی خودکار برچسب‌های سرآیند ID است. فرمان co در rcs یک تعویض پارامتر از برخی کلید واژه‌های رزرو شده انجام می‌دهد، برای مثال، تعویض ‎# $Id$‎ در یک اسکریپت با چیزی مانند:

  • # $Id: hello-world.sh,v 1.1 2004/10/16 02:43:05 bozo Exp $

‎36.7.2‎- ‏Widgetها

توانایی فراخوانی کردن widgetهای ‎X-Windows‎ از یک اسکریپت پوسته، مطلوب است. اتفاقاً چند بسته با نام‌های Xscript‏، Xmenu‏، و widtools وجود دارد که مدعی انجام این کار هستند. دو مورد نخست اینها به نظر می‌رسد دیگر پشتیبانی نمی‌شوند. خوشبختانه باز هم تهیه widtools از اینجا امکان‌پذیر است.

Caution

بسته widtools ‎(widget tools)‎ احتیاج دارد که کتابخانه XForms نصب شده باشد. به طور اضافی، قبل از اینکه بسته روی یک سیستم لینوکس نوعی نصب بشود Makefile نیازمند مقداری ویرایش با تدبیر است. سرانجام، سه مورد از شش ویجت ارایه شده کار نمی کنند (و در واقع با خطای segfault مواجه می‌گردند).

خانواده ابزارهای dialog روشی برای فراخوانی ویجت‌های «dialog» از یک اسکریپت پوسته را ارایه می‌کنند. برنامه سودمند اصلی dialog در یک کنسول متنی کار می‌کند، اما جانشین های آن، gdialog‏، Xdialog‏، و kdialog مجموعه ویجت‌های مبتنی بر پنجره X را به کار می‌برند.

مثال ‎36-22‎. ویجت‌های فراخوانی شده از یک اسکریپت پوسته

#!/bin/bash
# dialog.sh: استفاده از ویجت‌های gdialog.

#       برای اجرای این اسکریپت باید gdialog روی سیستم شما نصب باشد.
# یا، می‌توانید تمام نمونه‌های «gdialog» پایین را با kdialog عوض کنید
# نگارش ‎1.1‎ (تصحیح شده ‎04/05/05‎)

#           این اسکریپت الهام گرفته از گفتار زیر است.
#  "Scripting for X Productivity," by Marco Fioretti,
# LINUX JOURNAL, Issue 113, September 2003, pp. 86-9.
#     از تمام شما دوستان خوب در ‎LINUX JOURNAL‎ متشکرم.

#                    خطای ورودی در کادر dialog.
E_INPUT=85
#                    ابعاد نمایش ویجت‌های ورودی.
HEIGHT=50
WIDTH=60

#    نام فایل خروجی (تشکیل شده از نام اسکریپت)‏.
OUTFILE=$0.output

#             نمایش این اسکریپت در یک ویجت متن.
gdialog --title "Displaying: $0" --textbox $0 $HEIGHT $WIDTH

#         اکنون، آزمایش ذخیره ورودی در یک فایل.
echo -n "VARIABLE=" > $OUTFILE
gdialog --title "User Input" --inputbox "Enter variable, please:" \
$HEIGHT $WIDTH 2>> $OUTFILE


if [ "$?" -eq 0 ]
#          کنترل کردن وضعیت خروج عادت خوبی است.
then
  echo "Executed \"dialog box\" without errors."
else
  echo "Error(s) in \"dialog box\" execution."
            # یا به جای OK روی Cancel کلیک شده.
  rm $OUTFILE
  exit $E_INPUT
fi


#  اکنون، متغیر را بازیابی کرده و نمایش می‌دهیم.
. $OUTFILE   #        منبع کردن فایل ذخیره شده.
echo "The variable input in the \"input box\" was: "$VARIABLE""

rm $OUTFILE  #  پاکسازی به وسیله حذف فایل موقت.
#برخی کاربردها شاید نیازمند حفظ این فایل باشند.

exit $?

# تمرین: این اسکریپت را با استفاده از مجموعه ویجت zenity بازنویسی کنید.

فرمان xmessage یک شیوه ساده از ظاهر شدن یک پنجره پیغام/پرسش است. برای مثال:

xmessage Fatal error in script! -button exit

آخرین قلم در میان ویجت‌ها، zenity است. این برنامه سودمند ویجت‌ها-و-پنجره‌های محاوره ‎GTK+‎ ظاهر می‌کند، و به طور بسیار دلپسندی در داخل یک اسکریپت عمل می‌کند.

get_info ()
{
  zenity --entry       #          پنجره پرس و جو ظاهر می‌کند و ورودی کاربر
                       #+                را در خروجی استاندارد چاپ می‌کند.

                       # گزینه‌های ‎--calendar‎ و ‎--scale‎ را هم امتحان کنید.
}

answer=$( get_info )   #             ضبط خروجی استاندارد در متغیر ‎$answer‎

echo "User entered: "$answer""

برای سایر شیوه‌های اسکریپت‌نویسی با ویجت‌ها، Tk یا wish ( گرفته شده ازTcl)‏، PerlTk ‏(Perl با الحاقیه‌های Tk)‏، tksh ‏(ksh با الحاقیه‌های Tk)‏، XForms4Perl ‏(Perl با الحاقیه‌های XForms)‏، ‎Gtk-Perl‎ ‏(Perl با الحاقیه‌های Gtk)‏، یا PyQt ‏(Python با الحاقیه های Qt) را امتحان کنید.