فصل 7- بررسی‌ها

‎7.1‎- ساختارهای بررسی

  • یک ساختار ‎if/then‎ بررسی می‌کند که آیا وضعیت خروج لیستی از فرمانها 0 هست (چون 0 بواسطه قرارداد یونیکس به معنی «موفقیت» است)، و اگر چنین باشد، یک یا چند فرمان را اجرا می‌کند.

  • یک فرمان اختصاصی تست به نام ‎[‎ (کاراکتر خاص left bracket) وجود دارد. این فرمان مترادفی برای test است، و برای کارآیی بهتر، به صورت یک فرمان داخلی است. این فرمان شناسه‌هایش را به عنوان عبارتهای مقایسه یا بررسی‌های فایل در نظر می‌گیرد و نسبت به نتیجه مقایسه یک وضعیت خروج (صفر برای صحیح و یک برای غلط) برگشت می‌دهد.

  • Bash با نگارش 2.02، فرمان بررسی توسعه‌یافته ‎[[ ... ]]‎ را ارایه نمود، که مقایسه‌ها را در یک حالت بیشتر آشنا برای برنامه‌نویسان سایر زبانها انجام می‌دهد. توجه داشته باشید که ‎[[‎ یک کلمه‌کلیدی است، نه یک فرمان.

    Bash عبارت ‎[[ $a -lt $b ]]‎ را به عنوان یک عنصر منفرد مشاهده می‌کند، که یک وضعیت خروج برگشت می‌دهد.

  • ساختارهای ‎(( ... ))‎ و ‎let ...‎ بر حسب آنکه عبارت‌های حسابی مورد ارزیابی آنها به یک مقدار غیر صفر بسط یابد، یک وضعیت خروج صفر برگشت می‌دهند. بدین سبب این ساختارهای عبارت حسابی می‌توانند برای انجام مقایسه‌های حسابی به کار بروند.

  • (( 0 && 1 ))                 # منطقی AND
    echo $?     # 1     ***
    # And so ...
    let "num = (( 0 && 1 ))"
    echo $num   # 0
    # But ...
    let "num = (( 0 && 1 ))"
    echo $?     # 1     ***
    
    
    (( 200 || 11 ))              # منطقی OR
    echo $?     # 0     ***
    # ...
    let "num = (( 200 || 11 ))"
    echo $num   # 1
    let "num = (( 200 || 11 ))"
    echo $?     # 0     ***
    
    
    (( 200 | 11 ))               # بیتی OR
    echo $?                      # 0     ***
    # ...
    let "num = (( 200 | 11 ))"
    echo $num                    # 203
    let "num = (( 200 | 11 ))"
    echo $?                      # 0     ***
    
    # همان وضعیت خروج مانندِ عبارت ‎"let"‎ ساختار‎
    #+  .حسابی پرانتزهای دوگانه را برگشت می‌دهد‎

    Caution

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

    var=-2 && (( var+=2 ))
    echo $?                   # 1
    
    var=-2 && (( var+=2 )) && echo $var
                              # !نخواهد کرد ‎echo‎ را ‎$var‎

  • یک if نه فقط شرط‌های محصور شده در داخل bracketها بلکه هر فرمانی را می‌تواند بررسی کند.

  • if cmp a b &> /dev/null  # موقوف کردن خروجی‎
    then echo "Files a and b are identical."
    else echo "Files a and b differ."
    fi
    
    #  ‎"if-grep"‎ ساختار خیلی مفید‎
    # ----------------------------------- 
    if grep -q Bash file
      then echo "File contains at least one occurrence of Bash."
    fi
    
    word=Linux
    letter_sequence=inu
    if echo "$word" | grep -q "$letter_sequence"
    # .مانع خروجی می‌شود  ‎grep‎ برای ‎"-q"‎ گزینه‎
    then
      echo "$letter_sequence found in $word"
    else
      echo "$letter_sequence not found in $word"
    fi
    
    
    if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
      then echo "Command succeeded."
      else echo "Command failed."
    fi

  • این دو مثال آخر اهدایی از ‎Stéphane Chazelas‎ است.

مثال ‎7-1‎. کدام صحیح است؟

#!/bin/bash

#  :نکته‎
#  اگر مطمئن نیستید که یک شرط معین ممکن است چطور 
#+ .بررسی کنید ‎if-test‎ ارزیابی گردد، آن را در یک‎

echo

echo "Testing \"0\""
if [ 0 ]      # صفر
then
  echo "0 is true."
else          # ... وگرنه‎
  echo "0 is false."
fi            # 0 is true.

echo

echo "Testing \"1\""
if [ 1 ]      # یک
then
  echo "1 is true."
else
  echo "1 is false."
fi            # 1 is true.

echo

echo "Testing \"-1\""
if [ -1 ]     # منفی یک
then
  echo "-1 is true."
else
  echo "-1 is false."
fi            # -1 is true.

echo

echo "Testing \"NULL\""
if [ ]        # ‎NULL‎ (شرط تهی)‎
then
  echo "NULL is true."
else
  echo "NULL is false."
fi            # NULL is false.

echo

echo "Testing \"xyz\""
if [ xyz ]    # رشته
then
  echo "Random string is true."
else
  echo "Random string is false."
fi            # Random string is true.

echo

echo "Testing \"\$xyz\""
if [ $xyz ]   # تهی است، اما ‎$xyz‎ بررسی می‌کند آیا
              # .این فقط یک متغیر ارزش‌گذاری نشده است‎
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi            # Uninitialized variable is false.

echo

echo "Testing \"-n \$xyz\""
if [ -n "$xyz" ]            # .تصحیح بیشتر خُرده‌گیرانه
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi                          # Uninitialized variable is false.

echo


xyz=                        # .مقدار دهی شده، اما به کمیت تهی‎

echo "Testing \"-n \$xyz\""
if [ -n "$xyz" ]
then
  echo "Null variable is true."
else
  echo "Null variable is false."
fi                          # Null variable is false.


echo


                            # درست است؟ ‎"false"‎ چه وقت

echo "Testing \"false\""
if [ "false" ]              #  فقط یک رشته است ‎"false"‎ به نظر می‌رسد‎
then
  echo "\"false\" is true." #+ و آن را صحیح ارزیابی می‌کند
else
  echo "\"false\" is false."
fi                          # "false" is true.

echo

echo "Testing \"\$false\""  # .یکبار دیگر، متغیر ارزش‌گذاری نشده‎
if [ "$false" ]
then
  echo "\"\$false\" is true."
else
  echo "\"\$false\" is false."
fi                          # "$false" is false.
                            # .اکنون، ما نتیجه مورد انتظار را به دست می‌آوریم‎
#    را بررسی می‌کردیم؟ ‎"$true"‎ چه اتفاقی رخ می‌داد اگر ما متغیر مقداردهی نشدهِ

echo

exit 0

تمرین. رفتار مثال ‎7-1‎ بالا را تشریح کنید.

if [ condition-true ]
then
   command 1
   command 2
   ...
else  # ... وگرنه‎
      # .افزودن قطعه کد اجرایی برای اینکه اگر نتیجه ارزیابی شرط اولیه غلط باشد
   command 3
   command 4
   ...
fi

وقتی در یک شرط بررسی if و then در یک سطر هستند، یک سمی‌کالن(;) باید جمله if را خاتمه بدهد. if و then هر دو کلمه کلیدی هستند. کلمه‌کلیدی‌ها (یافرمانها) جمله‌ها را شروع می‌کنند، و قبل از شروع یک جمله جدید در همان سطر، جمله قبلی باید خاتمه بیابد.

if [ -x "$filename" ]; then

else if و elif

elif

elif یک حالت فشرده برای else if است. نتیجه‌اش، جای گرفتن یک ساختار if/then درونی در میان یک ساختار بیرونی است.

if [ condition1 ]
then
   command1
   command2
   command3
elif [ condition2 ]
#  ‎else if‎ همانطور مانند‎
then
   command4
   command5
else
   default-command
fi

ساختار ‎if test condition-true‎ معادل دقیقِ ‎if [ condition-true ]‎ است. اتفاقاً، bracket سمت چپ، ‎[‎، یک علامت مشخصه است [1] که فرمان test را احضار می‌کند. بنابراین bracket بسته سمت راست، ‎]‎ در یک ‎if/test‎ به طور صریح نباید نیاز باشد، اما نگارش‌های جدیدتر Bash آن را لازم دارند.

فرمان test یک فرمان داخلی Bash است که بررسی نوع فایلها و مقایسه رشته‌ها را انجام می‌دهد. بنابراین، در یک اسکریپت Bash، فرمان test، برنامه باینری خارجی ‎/usr/bin/test‎ را که قسمتی از بسته sh-utils است، فراخوانی نمی‌کند. به همچنین، ‎[‎ نیز ‎/usr/bin/[‎ را که به ‎/usr/bin/test‎ پیوند می‌شود، فراخوانی نمی‌کند.

bash$ type test
test is a shell builtin
bash$ type '['
[ is a shell builtin
bash$ type '[['
[[ is a shell keyword
bash$ type ']]'
]] is a shell keyword
bash$ type ']'
bash: type: ]: not found

اگر به دلایلی شما مایل هستید از‎/usr/bin/test‎ در یک اسکریپت Bash استفاده کنید، پس به وسیله مسیر کامل آن را مشخص نمایید.

مثال ‎7-2‎. هم‌ارزی test‏، ‎/usr/bin/test‎‎،‏ ‎[‎‎]‎‎‏، و ‎/usr/bin/[‎

#!/bin/bash

echo

if test -z "$1"
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi
echo

if /usr/bin/test -z "$1"      #  ‎"test"‎ معادل فرمان داخلی‎
#  ‎^^^^^^^^^^^^^‎              #     .مشخص کننده مسیر کامل‎
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if [ -z "$1" ]                #   از نظر وظیفه، معادل قطعه کدهای فوق است‎
#   if [ -z "$1"              #     ،با یک پیعام خطا ‎Bash‎ کار می‌کند، اما‎
                              #+ .بسته واکنش نشان می‌دهد‎ bracket‎ به فقدان
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if /usr/bin/[ -z "$1" ]       #    .بازهم، از نظر کارکرد، معادل موارد فوق است
# if /usr/bin/[ -z "$1"       #           .کار می‌کند؟، اما یک پیغام خطا می‌دهد‎
                              #  اصلاح گردیده است ‎3.x.‎ نگارش ‎Bash‎ این در ‎:‎توجه
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

exit 0

به دنبال یک if، نه فرمان test و نه bracketهای تست ‎( [ ] یا [[ ]] )‎ به طور سخت‌گیرانه لازم نیستند.
dir=/home/bozo

if cd "$dir" 2>/dev/null; then   #پیغام خطا را پنهان می‌کند ‎2>/dev/null‎‎
  echo "Now in $dir."
else
  echo "Can't change to $dir."
fi
ساختار ‎"if COMMAND"‎ وضعیت خروج COMMAND را برگشت می‌دهد.

به طور مشابهی، یک شرط داخل براکت‌های test موقعی که در ترکیب با یک ساختار لیست استفاده شوند، می‌توانند به تنهایی و بدون یک if باشند.

var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"

home=/home/bozo
[ -d "$home" ] || echo "$home directory does not exist."

ساختار ‎(( ))‎ یک عبارت حسابی را بسط داده و ارزیابی می‌کند. اگر عبارت صفر ارزیابی شود، یک وضعیت خروجِ 1، یا «غلط» برگشت می‌دهد. یک عبارت غیر‌صفر وضعیت خروج 0، یا «صحیح» برگشت می‌دهد. این نقطه مقابل استفاده از ساختارهای test و ‎[‎ ‎]‎ است که قبلاً بحث شد.

مثال ‎7-3‎. بررسی‌های حسابی با استفاده از ‎(( ))‎

#!/bin/bash
# arith-tests.sh
# بررسی‌های حسابی‎
# .عبارت‌های عددی را بررسی و ارزیابی می‌کند ‎(( ... ))‎ ساختار‎
#                       ‎[ ... ]‎ وضعیت خروج متضاد با ساختار‎

(( 0 ))
echo "Exit status of \"(( 0 ))\" is $?."          # 1

(( 1 ))
echo "Exit status of \"(( 1 ))\" is $?."          # 0

(( 5 > 4 ))                                       # true
echo "Exit status of \"(( 5 > 4 ))\" is $?."      # 0

(( 5 > 9 ))                                       # false
echo "Exit status of \"(( 5 > 9 ))\" is $?."      # 1

(( 5 == 5 ))                                      # true
echo "Exit status of \"(( 5 == 5 ))\" is $?."     # 0
# (( 5 = 5 ))  gives an error message.

(( 5 - 5 ))                                       # 0
echo "Exit status of \"(( 5 - 5 ))\" is $?."      # 1

(( 5 / 4 ))                                       # Division o.k.
echo "Exit status of \"(( 5 / 4 ))\" is $?."      # 0

(( 1 / 2 ))                                       # .نتیجه تقسیم کوچکتر از یک است‎
echo "Exit status of \"(( 1 / 2 ))\" is $?."      #           به صفر گِرد شده است
                                                  # 1

(( 1 / 0 )) 2>/dev/null                           #         تقسیم بر صفر نامعتبر
#           ^^^^^^^^^^^
echo "Exit status of \"(( 1 / 0 ))\" is $?."      # 1

#               دارای چه اثری است؟ ‎"2>/dev/null"
#            اگر حذف شده بود چه اتفاقی رخ می‌داد؟‎
#   آنرا حذف، و سپس اجرای اسکریپت را امتحان کنید‎

# ============================================ #
#  .سودمند است ‎if-then‎ نیز در یک بررسی ‎(( ... ))‎‎

var1=5
var2=4
if (( var1 > var2 ))
then # ‎^      ^‎      چرا؟ ‎$var1‎ نه ‎$var2‎ ‎نه :‎توجه
  echo "$var1 is greater than $var2"
fi                #          5 is greater than 4‎
exit 0

یادداشت‌ها

[1]

علامت مشخصه یک نماد یا رشته کوتاه با یک معنی ویژه وابسته به آن ( یک معنی فراتر) است. در Bash، برخی علامت‌ها، از قبیل ‎[‎ و . (dot-command)، می‌توانند به کلیدواژه‌ها و فرمانها بسط بیابند.