[(فرمان test) و [[ (فرمان جدید test) برای ارزیابی عبارتها به کار میروند. [[ فقط در پوسته Bash و Korn کار میکند، و قدرتمندتر میباشد، [ و test در پوستههای POSIX معتبر هستند. چند مثال در اینجا آمده است:
if [ -z "$variable" ] then echo "variable is empty!" fi if [ ! -f "$filename" ] then echo "not a valid, existing file name: $filename" fi
و
if [[ ! -e $file ]] then echo "directory entry does not exist: $file" fi if [[ $file0 -nt $file1 ]] then echo "file $file0 is newer than $file1" fi
برای کوتاهی گفتار: test ترکیب قدیمی و قابل حمل فرمان را اجرا میکند. تقریباً در تمام پوستهها(قدیمیترین پوستههای Bourne استثنا هستند)، [ مترادف test است(اما به یک شناسه انتهایی ] احتیاج دارد). هر چند تمام پوستههای مدرن دارای پیادهسازی داخلی [ میباشند، به طور معمول بازهم یک برنامه اجرایی خارجی با آن نام وجود دارد، به عنوان مثال /bin/[. استاندارد POSIX یک مجموعه ویژگی الزامی برای [ تعریف نموده است، اما تقریباً هر پوستهای ملحقاتی را با آن ارائه میکند. بنابراین، اگر کُد قابل حمل میخواهید، باید مراقب باشید که آن ملحقات را به کار نبرید.
[[ نگارش بهبودیافتهای از آن است، و یک کلمه کلیدی است، نه یک برنامه. این موضوع استفاده از آن را به طوری که در ذیل نشان داده شده آسانتر میسازد. [[ توسط پوسته Korn و BASH (به عنوان مثال نگارش 2.03) شناخته میشود، اما توسط پوستههای قدیمیتر POSIX یا پوسته Bourne خیر.
اگر چه [ و [[ اشتراک فراوانی دارند، و در عملگرهای بسیاری مانند "-f", "-s", "-n", "-z" با هم سهیم میباشند، تفاوتهای قابل توجهی نیز وجود دارد. این هم یک لیست مقایسهای:
ویژگی | تست جدید [[ | تست قدیمی [ | مثال |
مقایسه رشتهای |
> |
\> (*) |
[[ a > b ]] || echo "a does not come before b" |
< |
\< (*) |
[[ az < za ]] && echo "az comes before za" |
|
= یا == |
= |
[[ a == a ]] && echo "a equals a" |
|
!= |
!= |
[[ a != b ]] && echo "a is not equal to b" |
|
مقایسه عددی |
-gt |
-gt |
[[ 5 -gt 10 ]] || echo "5 is not bigger than 10" |
-lt |
-lt |
[[ 8 -lt 9 ]] && echo "8 is less than 9" |
|
-ge |
-ge |
[[ 3 -ge 3 ]] && echo "3 is greater than or equal to 3" |
|
-le |
-le |
[[ 3 -le 8 ]] && echo "3 is less than or equal to 8" |
|
-eq |
-eq |
[[ 5 -eq 05 ]] && echo "5 equals 05" |
|
-ne |
-ne |
[[ 6 -ne 20 ]] && echo "6 is not equal to 20" |
|
ارزیابی شرطی |
&& |
-a (**) |
[[ -n $var && -f $var ]] && echo "$var is a file" |
|| |
-o (**) |
[[ -b $var || -c $var ]] && echo "$var is a device" |
|
گروهبندی عبارت |
(...) |
\( ... \) (**) |
[[ $var = img* && ($var = *.png || $var = *.jpg) ]] && |
انطباق الگو |
= یا == |
نا معتبر |
[[ $name = a* ]] || echo "name does not start with an 'a': $name" |
انطباق عبارت منظم |
=~ |
نا معتبر |
[[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th!" |
(*) این یک پیوست به POSIX استاندارد است، برخی پوستهها شاید آن را داشته باشند و برخی نداشته باشند.
(**) عملگرهای -a و -o، و گروهبندی ( ... )، در POSIX تعریف شدهاند اما فقط برای وضعیت های شدیداً محدود. استفاده از اینها تشویق نمیشود، شما باید به جای آن از چندین [ استفاده کنید:
if [ "$a" = a ] && [ "$b" = b ]; then ...
if { [ "$a" = a ] || [ "$b" = b ] ; } && [ "$c" = c ]; then ...
گزینههای اولیهای که در [[ تعریف شده، اما شاید [ فاقد آن باشد(بسته به پیادهسازی):
شرح |
گزینهها |
مثال |
ورودی(فایل یا شاخه) موجود است |
-e |
[[ -e $config ]] && echo "config file exists: $config" |
فایل جدیدتر یا قدیمیتر از دیگری است |
-nt یا -ot |
[[ $file0 -nt $file1 ]] && echo "$file0 is newer than $file1" |
هر دو فایل یکسان هستند |
-ef |
[[ $input -ef $output ]] && { echo "will not overwrite input file: $input"; exit 1; } |
نفی کردن |
! |
[[ ! -u $file ]] && echo "$file is not a setuid file" |
لیکن تفاوتهای ظریف بیشتری وجود دارد.
تفکیک کلمه یا بسط glob بواسطه [[ انجام نخواهد شد(و بنابراین بسیاری از شناسهها نیازی به نقلقولی شدن ندارند):
file="file name" [[ -f $file ]] && echo "$file is a file"
با وجود اینکه $file نقلقولی نشده و شامل فضای سفید میباشد، کار میکند. با [ لازم است متغیر نقلقولی بشود:
file="file name" [ -f "$file" ] && echo "$file is a file"
این امر استفاده از [[ را آسانتر و استعداد خطا را کمتر میکند.
در [[ نیازی به پوشش دادن(با کاراکتر گریز) پرانتزها نیست:
[[ -f $file1 && ( -d $dir1 || -d $dir2) ]] [ -f "$file1" -a \( -d "$dir1" -o -d "$dir2" \) ]
از bash نگارش 4.1 مقایسه رشته با استفاده از < یا > وقتی در [[ انجام میشود منطقه جاری لحاظ میشود، اما در [ یا test لحاظ نمیشود. در حقیقت، [ و test، حتی اگر در صفحات man گفته باشد انجام میدهد، هرگز مرتبسازی منطقهای به کار نبردهاند. در نگارشهای Bash قبل از 4.1 نیز [[ مرتبسازی نسبت به منطقه به کار نمیبرد.
به عنوان یک قاعده کلی، [[ برای رشتهها و فایلها به کار میرود. اگر میخواهید اعداد را مقایسه کنید، از یک عبارت محاسباتی استفاده کنید، به عنوان نمونه
# Bash i=0 while ((i<10)); do ...
چه وقت باید فرمان تست جدید [[ به کار برود، و چه موقع فرمان قدیمی [؟ اگر قابلیت حمل به BourneShell اهمیت دارد، باید ترکیب قدیمی استفاده گردد. از طرف دیگر ، اگر اسکریپت به BASH یا KornShell احتیاج دارد، ترکیب جدید خیلی بیشتر قابل انعطاف است.
فصل شرط ها و بررسیها در راهنمای Bash را ببینید.
نظریه پشت تمام این مطالب آنست که [ فرمان سادهایست، در جاییکه [[ فرمانی مرکب است. [ شناسههایش را مانند هر فرمان دیگری دریافت میکند، اما اکثر فرمانهای مرکب زمینه تجزیه خاصی را ارائه میکنند که قبل از هرگونه پردازش دیگری انجام میشود. به طورنوعی این مرحله کلمات رزرو شده یا عملگرهای کنترلی مخصوص هر فرمان مرکب که آن را به قسمتهایی تجزیه میکند یا بر روند کنترل اثر میگذارد را جستجو میکند. عملگرهای and و or بررسی منطقی عبارت میتوانند دور زده شوند، زیرا در این روش آنها خاص هستند(به عنوان مثال مانند ;;، elif، و else هستند). بر خلاف عبارت حسابی، که به طور معمول در آن تمام بسطها از چپ به راست، با رشته نتیجهای که به عنوان موضوع محاسباتی تفسیر میشود، انجام میگردند.
# Bash (( 1 + 1 == 2 ? 1 : $(echo "آنچه شما فکر میکنید را انجام نمیدهد" >&2; echo 1) ))
# Bash [[ '1 + 1' -eq 2 && $(echo "اما این احتمالاً آنچه را انتظار دارید انجام میدهد" >&2) ]]
# Bash [ $((1 + 1)) -eq 2 -o $(echo 'No short-circuit' >&2) ]
احتمالاً قبل از انجام بسط ها، با جستجوی نشانههای ویژه فرمان مرکب، مدیریت خطای متفاوتی امکان پذیر میشود. [[ میتواند حضور بسطهایی که یک کلمه نتیجه نمیدهند ولی بازهم اگر مشخص نشوند یک خطا میسازند را تشخیص بدهد. دستورات معمولی نمیتوانند.
# Bash ( set -- $(echo 'بسطهای غیر نقلقولی تهی پارامترهای تهی را نتیجه نمیدهند' >&2); echo $# ) [[ -z $(:) ]] && echo ' به عنوان یک شناسه فراهم شده و تهی ارزیابی نمیشود-z ' [ -z ] && echo ' به عنوان یک شناسه فراهم نشده و خطایی گزارش نمیکند-z' #را مجبور به تعیین یک شناسه نماید نیست Bash در اینجا راهی برای آنکه بتواند [[ -z ]] #.این موجب یک خطا خواهد شد که دستورات معمولی نمیتوانند تشخیص بدهند
به دلیل بسیار مشابهی، زیرا عملگرهای [ درست مثل شناسهها هستند، برخلاف [[، شما میتوانید برای فرمان معمولیtest عملگرها را به عنوان پارامترها تعیین نمایید. ممکن است این یک محدودیت برای [[ به نظر برسد، اما تقریباً همیشه بخش زیرین نسبتاً میچربد.
# ksh93 args=('0' '-gt' '1') (( $(print '0 > 1') )) # دستور معتبر، مطابق انتظار، وضعیت خروج ۱ است [ "${args[@]}" ] # این هم با ۱ خارج میشود [[ ${args[@]} ]] # دستور معتبر، اما گمراه کننده. وضعیت خروج صفر است # میباشد [[ -n '0 -gt 1 ]] آشکار میکند که فرمان حاصل از بسط، set -x دستور
# ksh93 typeset -i x=0 ( print "$(( ++x, ${ x+=1; print $x >&2;}1, x ))" ) # را چاپ میکند 1,2 ( print "$(( $((++x)), ${ x+=1; print $x >&2;}1, x ))" ) # را چاپ میکند 2,2 # به دلیل آنکه اول بسط انجام میشود
پرسش و پاسخ 31 (آخرین ویرایش 2012-05-27 19:22:57 توسط cpc18-rdng20-2-0-cust501)