8. دستور Test

در هر اسکریپت پوسته نوشته شده Test به طور ضمنی به کار رفته‌است. ممکن است اینطور به نظر نرسد، اما test اغلب به طور مستقیم فراخوانی نمی‌شود. test به طور مکرر به صورت ] نامیده می‌شود. دستور ] یک پیوند نمادین به دستور test، می‌باشد،فقط برای آن که برنامه شل را بیشتر قابل خواندن کند. گر چه به طور معمول دستور داخلی پوسته نیز می‌باشد( و به این معنی است، که حتی اگر محیط یونیکس شما به طور متفاوتی تنظیم شده‌باشد، پوسته خودش ‎[‎ را به عنوان test تفسیر می‌کند):

$ type [
[ is a shell builtin
$ which [
/usr/bin/[
$ ls -l /usr/bin/[
lrwxrwxrwx 1 root root 4 Mar 27 2000 /usr/bin/[ -> test

نتیجه این که ‎ '['‎به راستی یک برنامه ،دقیقاًمانند ls و سایر برنامه‌ها می‌باشد، بنابراین باید در اطراف آن فاصله گذاری شود:

if [$foo == "bar" ]

کد فوق کار نمی‌کند، چون به صورت‎ if test$foo == "bar" ]‎ تفسیر می‌شود، که یک ‎ ']' ‎ بدون حضور ‎ '['‎ می‌باشد. در اطراف تمام عملگرها فاصله بگذارید، من فاصله‌های الزامی را باقرار دادن کلمه 'SPACE' برجسته کرده‌ام - شما اینها را با یک کاراکتر واقعی فاصله جایگزین کنید، اگر یک فاصله در آن‌جا نباشد، کار نخواهد کرد:

if SPACE [ SPACE "$foo" SPACE == SPACE "bar" SPACE ]

دستور Test برنامه مقایسه ساده اما قدرتمندی است. برای اطلاعات کامل، دستور man test را در سیستم خود اجرا کنید، اما در این‌جا برخی موارد استفاده و مثال‌های نمونه آمده است.

Test اکثر اوقات به طورغیر مستقیم به واسطه دستورات if و while احضار می‌شود. به همین دلیل اگر شما برنامه‌ای به نام test ایجاد کنید، وسعی در اجرای آن نمایید،با مشکلات مواجه می‌گردید، چون این دستور داخلی پوسته به جای برنامه شما فرا خوانده می‌شود!
ترکیب دستوری ‎ if...then...else...‎ چنین است:

if [ ... ]
then
  # if-code
else
  # else-code
fi

توجه کنید که fi وارونه if است! این وارونگی بعداً دوباره با case و esac هم به کار رفته است .
همچنین آگاه باشید که، ترکیب دستوری ‎ "if [ ... ]" ‎ و دستور then باید در دو سطر مختلف باشند. به طور جایگزین، کاراکتر سمی کالن ‎ ";" ‎ می‌تواند آن‌ها را ازیکدیگر جدا نماید:

if [ ... ]; then
  # do something
fi

همچنین می‌توانید از elif، به صورت زیر استفاده کنید:

if  [ something ]; then
 echo "Something"
 elif [ something_else ]; then
   echo "Something else"
 else
   echo "None of the above"
fi

بااجرای آن، اگر آزمون ‎ [ something ]‎ موفق باشد، «Something» را نمایش می‌دهد، در غیر آن‌صورت، ‎ [ something_else ] ‎  را بررسی می‌کند، و اگر موفق باشد «Something else» را نمایش می‌دهد . اگر همه موارد ناموفق باشد، عبارت «None of the above» را نمایش خواهد داد.

کد کوچک زیر را امتحان کنید، قبل از اجرای آن مقادیر متنوعی، مانند، ‎ -1 ‎ و 0، 1, hello، bye، و غیره را به متغیر X نسبت بدهید. می‌توانید آن را به صورت زیر انجام دهید ( با تشکر از Dave برای توجه دادن به لزوم export متغیر، به صورتی‌که در بخش متغیرها - قسمت اول اشاره شده.):

$ X=5
$ export X
$ ./test.sh
  ... output of test.sh ...
$ X=hello
$ ./test.sh
  ... output of test.sh ...
$ X=test.sh
$ ./test.sh
  ... output of test.sh ...

بعد دومرتبه با X$ به عنوان نام یک فایل موجود، از قبیل ‎/etc/hosts‎ ، آزمایش کنید.


test.sh
#!/bin/sh
if [ "$X" -lt "0" ]
then
  echo "X is less than zero"
fi
if [ "$X" -gt "0" ]; then
  echo "X is more than zero"
fi
[ "$X" -le "0" ] && \
      echo "X is less than or equal to  zero"
[ "$X" -ge "0" ] && \
      echo "X is more than or equal to zero"
[ "$X" = "0" ] && \
      echo "X is the string or number \"0\""
[ "$X" = "hello" ] && \
      echo "X matches the string \"hello\""
[ "$X" != "hello" ] && \
      echo "X is not the string \"hello\""
[ -n "$X" ] && \
      echo "X is of nonzero length"
[ -f "$X" ] && \
      echo "X is the path of a real file" || \
      echo "No such file: $X"
[ -x "$X" ] && \
      echo "X is the path of an executable file"
[ "$X" -nt "/etc/passwd" ] && \
      echo "X is a file which is newer than /etc/passwd" 

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

به طوری که در این مثال می‌بینیم test می‌تواند، آزمون‌های بسیاری روی اعداد، رشته‌ها، و فایل‌ها، انجام دهد.

با تشکر از Aaron برای نشان دادن آن که موارد ‎-a‎ و ‎ -e‎ ( هر دو به معنی «فایل موجود است») و ‎-S‎ (فایل یک سوکت است) و ‎-nt‎ (فایل جدیدتر از ... است) ‎-ot‎ (فایل قدیمی‌تر از ... است) و ‎-ef‎ (مسیرها به همان فایل ارجاع می‌دهد) و ‎-O‎ (فایل در مالکیت من است)، در پوسته سنتی بورن در دسترس نمی‌باشد ( مثال: ‎ /bin/sh ‎ در سولاریس، AIX، HPUX، و غیره ).

روش ساده‌تری برای نوشتن دستورات if وجود دارد: استفاده از  && و || در کد، برای آن که درصورت صحیح بودن نتیجه، اجرا ادامه یابد.

#!/bin/sh
[ $X -ne 0 ] && echo "X isn't zero" || echo "X is zero"
[ -f $X ] && echo "X is a file" || echo "X is not a file"
[ -n $X ] && echo "X is of non-zero length" || \
     echo "X is of zero length"

این ترکیب دستوری به علت وجود یک فایل( یا دستور داخلی ) به نام ‎[‎ که به test لینک شده، وجود دارد. در کاربرد این ساختار، دقیق باشید، به هرحال، استفاده مفرط می‌تواند به دشواری خواندن کد، منجر گردد. ترکیب if...then...else... خیلی بیشتر قابل خواندن است. استفاده از ساختار [...] برای حلقه‌های while و کنترل‌هایی با عقلانیت کم، که نمی‌خواهید با آن موجب حواس‌پرتی بالای خوانندگان بشوید، توصیه شده است.

به چند پیغام اول حاصل از مقایسه‌ها، موقعی که به متغیر X کمیت‌های غیر عددی نسبت دادید، توجه کنید:

test.sh: [: integer expression expected before -lt
test.sh: [: integer expression expected before -gt
test.sh: [: integer expression expected before -le
test.sh: [: integer expression expected before -ge
به آن دلیل اینطور است که، ‎-lt‎، ‎-gt‎،‎ -le‎، ‎-ge‎فقط برای اعداد صحیح طراحی شده‌اند و با رشته ها کار نمی‌کنند. مقایسه‌های رشته‌ای، ازقبیل ‎ !=‎ رضایتمندانه با "5" به عنوان یک رشته رفتار می‌کنند، اماراه معقولی برای رفتار با "Hello" به عنوان عدد صحیح وجود ندارد، بنابراین، ازمقایسه عدد صحیح شکایت می‌کند.
اگر می‌خواهید اسکریپت شما به طور دلپذیر‌تر، رفتار کند، باید قبل از به کار بردن متغیر در test محتویات آن را کنترل کنید - احتمالاً کاری مانند این:
echo -en "Please guess the magic number: "
read X
echo $X | grep "[^0-9]" > /dev/null 2>&1
if [ "$?" -eq "0" ]; then
  # If the grep found something other than 0-9
  # then it's not an integer.
  echo "Sorry, wanted a number"
else
  # The grep found only 0-9, so it's an integer. 
  # We can safely do a test on it.
  if [ "$X" == "7" ]; then
    echo "You entered the magic number!"
  fi
fi

در این روش می‌توانید پیغام‌های بامعنی‌تری برای کاربر echo نمایید، و به طور دلپسندی خارج شوید. متغیر ‎$?‎ در بخش متغیرها - قسمت دوم توضیح داده شده، و grep هیولای پیچیده‌ای است،که چنین است:‎ grep [0-9]‎ سطرهایی ازمتن، که محتوی ارقام صفر تا نه و احتمالاً سایر کاراکترها می‌باشند را پیدا می‌کند،همین طور کاراکتر هشتک (^) در ‎ grep [^0-9]‎ فقط سطرهایی که محتوی اعداد( مترجم: دقت شود اعداد نه ارقام ) نیستند را می‌یابد. بعد می‌توانیم برعکس کنیم ( با اعمال روی موارد ناموفق ). خوب؟ جمله ‎ >/dev/null 2>&1‎ هر خروجی یا پیغام خطایی را، به جای ارسال به صفحه نمایش کاربر به دستگاه ویژه‎ "null"‎ تغییر جهت می‌دهد.
بسیار تشکر می‌کنم از Paul Schermerhorn برای تصحیح اشتباهم - در این‌جا باید مطرح می‌شد که ‎grep -v [0-9]‎ کار می‌کند، فقط به طور واضحی سهل گرفتن زیاد است.

می‌توانیم از test درحلقه‌های whileبه صورت پایین استفاده کنیم:


test2.sh
#!/bin/sh
X=0
while [ -n "$X" ]
do
  echo "Enter some text (RETURN to quit)"
  read X
  echo "You said: $X"
done

این کد، پرسشی را برای ورودی، تا زدن کلید اینتر روی صفحه نمایش می‌دهد( طول X صفر است). باتشکر از Justin Heath برای نشان دادن آن که اسکریپت کار نمی‌کرد - من نقل‌قول‌های اطراف ‎ $X ‎ را در جمله ‎ while [ -n "$X" ] ‎  از قلم انداخته بودم. بدون آن نقل‌قول‌ها،موقعی که ‎ $X ‎ تهی است، چیزی برای بررسی توسط test وجود ندارد.
Alexander Weber اشاره نموده است که اجرای این اسکریپت به طور نامرتبی پایان می‌پذیرد:

$ ./test2.sh
Enter some text (RETURN to quit)
fred
You said: fred
Enter some text (RETURN to quit)
wilma
You said: wilma
Enter some text (RETURN to quit)

You said:
$ 

با بررسی(test)دیگری در داخل حلقه می‌تواند پاکیزه شود:

#!/bin/sh
X=0
while [ -n "$X" ]
do
  echo "Enter some text (RETURN to quit)"
  read X
  if [ -n "$X" ]; then
    echo "You said: $X"
  fi
done

همچنین توجه داشته باشید که، من دو ترکیب دستوری متفاوت برای حکم if در این صفحه استفاده نموده‌ام. آنها چنین هستند:

if [ "$X" -lt "0" ]
then
  echo "X is less than zero"
fi

..........  and  ........

if [ ! -n "$X" ]; then
  echo "You said: $X"
fi

شما باید یک انقطاع بین دستور if و ساختار then داشته باشید. این می‌تواند یک سمی‌کالن یا سطرجدید باشد، مهم نیست که کدام است،اما باید، یکی یا دیگری بین if و then باشد . دلپذیر خواهد بود،که فقط بگوییم:

if [ ! -n "$X" ]
  echo "You said: $X"

اما then و fi به طور مطلق لازم می‌باشند.

Steve Parker  نوشته  Bourne و Bash راهنمای آموزشی اسکریپت نویسی
لطفاً برای بهتر دیدن صفحه از فایرفاکس استفاده کنید