ساختارهای بررسی

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

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

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

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

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

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

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

  • (( 0 && 1 ))                 #
    echo $?     #
    #
    let "num = (( 0 && 1 ))"
    echo $num   #
    #
    let "num = (( 0 && 1 ))"
    echo $?     #
    
    
    (( 200 || 11 ))              #
    echo $?     #
    #
    let "num = (( 200 || 11 ))"
    echo $num   #
    let "num = (( 200 || 11 ))"
    echo $?     #
    
    
    (( 200 | 11 ))               #
    echo $?                      #
    #
    let "num = (( 200 | 11 ))"
    echo $num                    #
    let "num = (( 200 | 11 ))"
    echo $?                      #
    
    #
    #+

    Caution

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

    var=-2 && (( var+=2 ))
    echo $?                   #
    
    var=-2 && (( var+=2 )) && 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 -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"
    #
    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

#
#
#+

echo

echo "Testing \"0\""
if [ 0 ]      #
then
  echo "0 is true."
else          #
  echo "0 is false."
fi            #

echo

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

echo

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

echo

echo "Testing \"NULL\""
if [ ]        #
then
  echo "NULL is true."
else
  echo "NULL is false."
fi            #

echo

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

echo

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

echo

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

echo


xyz=                        #

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


echo


                            #

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

echo

echo "Testing \"\$false\""  #
if [ "$false" ]
then
  echo "\"\$false\" is true."
else
  echo "\"\$false\" is false."
fi                          #
                            #
#  

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 ]
#
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"      #
#             #    
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if [ -z "$1" ]                # 
#              #   
                              #+
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if /usr/bin/[ -z "$1" ]       #   
#      #          
                              #
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   #
  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
#
#
#
#                     

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

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

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

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

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

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

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

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

(( 1 / 0 )) 2>/dev/null                           #        
#
echo "Exit status of \"(( 1 / 0 ))\" is $?."      #

#              
#           
#  

# 
#  

var1=5
var2=4
if (( var1 > var2 ))
then # 
  echo "$var1 is greater than $var2"
fi                #         
exit 0

یادداشت‌ها

[1]

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