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
به طور مطلق لازم میباشند.