آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

عبارت محاسباتی

عبارت محاسباتی

محاسبات در BASH فقط حساب اعداد صحیح است. شما نمی‌توانید حساب ممیز شناور در Bash انجام دهید، اگر آن توانایی را لازم دارید، پرسش و پاسخ شماره 22 را ببینید.

همچنین گفتار Bash hackers را در باره نظریه دستور زبان کامل، ملاحظه کنید.

/!\ ترکیب ‎$[ ]نکوهیده شده است.

برای آنکه به Bash بگوییم با اعداد به جای رشته، به عنوان اعداد صحیح رفتار نماید، و عملیات اصلی حساب را با آنها انجام بدهد، چند روش وجود دارد. اولین آنها استفاده از فرمان let است:

let a=17+23
echo "a = $a"      # را چاپ می‌کند a = 40 

توجه کنید که در هر تخصیص متغیر، اگر سمت راست تساوی شامل فاصله باشد، لازم است طرف سمت راست به طور کامل نقل‌قولی شود، از این قرار:

let a=17 + 23      # اشتباه
let a="17 + 23"    # صحیح

تقسیم در Bash تقسیم صحیح است، و درست مانند C نتیجه را کوتاه می‌کند:

let a=28/6
echo "a = $a"      # را چاپ می‌کند a = 4

علاوه بر فرمان let ، شاید کسی بخواهد از ‎ (( ))‎ برای تأکید نمودن بر یک زمینه محاسباتی استفاده نماید. اگر یک $ (علامت دلار) قبل از پرانتزها وجود داشته باشد، در آن صورت یک جایگزینی انجام می‌شود(مثال پایین‌تر از این). فضای خالی در داخل ‎ (( ))‎ با ملایمت بیشتری نسبت به تخصیص معمولی اجازه داده می‌شود، و متغیرهای داخل ‎ (( ))‎ نیازی به علامت $ ندارند( به علت آنکه رشته‌های لفظی مجاز نیستند). مثالها:

((a=$a+7))         #  اضافه می‌کند a  را به مقدار‎ 7 ‎      
((a = a + 7))      # اضافه می‌کند a  را به مقدار‎ 7 ‎  مانند مثال قبل
((a += 7))         # اضافه می‌کند a  را به مقدار‎ 7 ‎  مانند مثال قبل 

((a = RANDOM % 10 + 1))     # یک عدد تصادفی از ۱ تا ده را انتخاب می‌کند‎
                            #  باقیمانده است C  همانند %
‏(( )) ‏همچنین ممکن است به عنوان فرمان به‌کار رود. علامت > یا< داخل (( )) به معنی #
کوچکتر یا بزرگتر از، هستند نه تغییر مسیر ورودی خروجی #‏ 
‎if ((a > 5)); then echo "a is more than 5"; fi‎

(( )) بدون علامت $ مقدم بر آن فقط ویژه Bash می‌باشد. جایگزینی ‎$(( ))‎ در پوسته POSIX مجاز است. به طوری که شاید انتظار برود، در فرمان اصلی، نتیجه عبارت حسابی داخل ‎$(( ))‎ جایگزین می‌گردد. این هم چند مثال از ترکیب دستوری جایگزینی حسابی:

a=$((a+7))                                 # مثال قبل POSIXنگارش سازگار با
if test $((a%4)) = 0; then ...
lvcreate -L $((4*1096)) -n lvname vgname   #  HP-UX مثال واقعی

با فرمان declare می‌توان متغیرها را به عنوان عددصحیح تعریف نمود، به طوری که هر تخصیص مقدار ثانوی آنها نیز همواره در یک زمینه عددی در نظر گرفته شود. هر متغیری که به عنوان عدد صحیح تعریف گردد، الزاماً همانطور عمل می‌کند که اگر موقع تخصیص مقدار به آن، یک فرمان let قبل از آن قرار داده باشید. برای مثال:

unset b             #  .فراموش نمودن هر تعریف قبلی
b=7+5; echo "$b"    #  .را چاپ می‌کند  ‎ 7+5 ‎
declare -i b        #  .را به عنوان عدد صحیح تعریف می‌کند b 
b=7+5; echo "$b"    #  .چاپ می‌شود‎ 12

همچنین، شاخص‌های آرایه زمینه عددی دارند:

n=0
while read line; do
   array[n++]=$line      #  .مجبور به زمینه عددی می‌گردد array[] 
done

یک تله رایج با عبارت‌های حسابی در Bash وجود دارد: اعداد با صفرهای پیشتاز(مترجم: دارای صفر پشت عدد هستند)، به عنوان عدد اکتال رفتار می‌کنند. به عنوان مثال

# فرض کنید امروز نوزدهم سپتامبر است
month=$(date +%m)
next_month=$(( (month == 12) ? 1 : month+1 ))
# bash: 09: value too great for base (error token is "09")

این باعث سردرگمی بسیاری در میان افرادی گردیده، که از منابع مختلفی(اگرچه رایج‌ترین مورد آن تاریخ است) اعداد با صفر پشت عدد را استخراج نموده‌اند و سپس بدون آنکه اول آنها را پاکسازی کنند، با آنها محاسبه کرده‌اند. ( مخصوصاً اگر شما چنین برنامه‌ای را در ماه مارس بنویسید و آنرا ارائه دهید، ... بعد در اول آگوست متوقف گردد.)

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

#  را حذف می‌کند  a این در هر نوبت یک صفر پشت عدد
while [[ $a = 0* ]]; do a=${a#0}; done

می‌توانید این کار را بدون کاربرد حلقه، با استفاده از جانشین‌های توسعه یافته نیز انجام بدهید، برای اطلاعات بیشتر پرسش و پاسخ شماره ۶۷ را ببینید. یا، می‌توانید از sed استفاده کنید، که اگر در حال خواندن اعداد بسیاری از یک جریان باشید، ممکن است کارآمدتر باشد، و می‌تواند به جای انجام یک به یک، تمام آنها را یکباره مرتب و تمیز کند.

بدون حلقه:

#  رابه یکباره حذف می‌کند a این صفرهای پشت عدد 
a=${a##+(0)}

سومین راه حل مجبور نمودن Bash به رفتار با تمام اعداد به عنوان عدد مبنای 10 از طریق پیشوند نمودن ‎ 10#‎ می‌باشد. شاید این کارآمدتر باشد، امابرای خواندن نیز کمتر برازنده است.

a=008
let b=a+1       # .یک خطا تولید می‌کند زیرا 008 عدد اکتال معتبری نیست
let b=10#$a+1   # .مجبور می‌کند در مبنای 10 عمل شود. توجه: وجود $ لازم است

سرانجام، یک تذکر در مورد وضعیت خروج فرمانها، و مفهوم صحیح و غلط، شایسته است. موقعی که bash دستوری را اجرا می‌کند، آن دستور یک کد وضعیت خروج بین 0 تا 255 برگشت می‌دهد. 0 موفقیت معنی می‌دهد( که وقتی در متن یک فرمان if یا while به کار برود به معنی صحیح است). هر چند در یک زمینه محاسباتی، قواعد زبان C صادق است(0 غلط است، هر چیز دیگری صحیح است).

چند مثال:

true; echo $?       # می‌نویسد 0، زیرا یک فرمان موفق  0 را برگشت می‌دهد.‎
((10 > 6)); echo $? # .همچنین 0.  یک دستور محاسبات نیز 0 برای صحیح
echo $((10 > 6))    # .می‌نویسد 1. یک عبارت محاسباتی برای صحیح  1 را برگشت می‌دهد

گذشته از مقایسه که برای صحیح 1 را برگشت می‌دهد، یک عبارت محاسباتی، که مقدارش غیرصفر ارزیابی شود نیز مانند یک فرمان، صحیح را برگشت می‌دهد.

if ((1)); then echo true; fi     #  .true می‌نویسد

این مورد به شما اجازه می‌دهد متغیرهای "flag" را درست همانطور که در برنامه C استفاده می‌شود، به کار ببرید:

found=0
while ...; do
  ...
  if something; then found=1; fi    # Found one!  Keep going.
  ...
done
if ((found)); then ...

این هم تابعی برای تبدیل اعداد از مبنای دیگر به دسیمال (مبنای 10):

todec() {
    echo $(( $1#$2 ))
}

مثالها:

todec 16 ffe    # -> 4094
todec 2 100100  # -> 36


CategoryShell

عبارت محاسباتی (آخرین ویرایش‎2013-05-13 17:44:38‎ توسط geirha)