مح

محاسبات در Bash

{one: "۱ تمرین", other: "%{count} تمرین"}

درباره‌ی محاسبات

Bash می‌تواند محاسبات انجام دهد، اما فقط با اعداد صحیح. اگر لازم است محاسبات اعشاری انجام دهید، باید به سراغ یک برنامه‌ی بیرونی بروید (مثل awk یا bc).

عملگرهای محاسباتی به ترتیب تقدم در راهنما فهرست شده‌اند: حساب در پوسته.

به‌علاوه، Bash فقط در جاهای مشخصی محاسبات انجام می‌دهد (که به آن‌ها «زمینه‌های محاسباتی» می‌گویند).

بسط محاسباتی

بسط محاسباتی شبیه جانشینی دستور است، اما با پرانتزهای دوتایی.

$(( expression ))

این نحوه‌ی نگارش به نتیجه‌ی ارزیابی عبارت بسط می‌یابد. معمولاً از آن برای ذخیره‌ی نتیجه در یک متغیر استفاده می‌شود:

x=$(( 3 + 4 ))
Note

میزان فاصله‌ی داخل پرانتزها به خودتان بستگی دارد. این موضوع به سلیقه و خوانایی مربوط است.

x=$(( 3 + 4 ))
y=$((5+6))

نویسنده‌ی مهربان شما دوست دارد فاصله اضافه کند تا عبارت محاسباتی داخل پرانتزها بهتر دیده شود.

شرط محاسباتی

شرط محاسباتی داخل پرانتزهای دوتایی و بدون $ ابتدایی اجرا می‌شود:

(( expression ))

این شکل به نتیجه بسط نمی‌یابد. اما یک «وضعیت خروج» تولید می‌کند.

  • اگر نتیجه‌ی عبارت غیرصفر باشد، وضعیت خروج ۰ (موفقیت) است.
  • اگر نتیجه‌ی عبارت صفر باشد، وضعیت خروج ۱ (شکست) است.
Note

داخل ((، Bash همان برداشت «درست»/«غلط» زبان C را به کار می‌برد: صفر «غلط» است و غیرصفر «درست». این دقیقاً برعکس روشی است که Bash وضعیت‌های خروج را با آن ارزیابی می‌کند!

معمولاً از این نحوه‌ی نگارش در دو جا استفاده می‌شود:

  1. به‌عنوان بخش COMMAND یک دستور if یا یک حلقه‌ی while.

    if (( x == y - 1 )); then ...
    while (( y > 0 )); do ...
    
  2. به‌عنوان یک دستور مستقل برای تغییر مقدار یک متغیر.

    (( x = x + 1 ))
    echo "new value for x is $x"
    
    Caution

    در استفاده از محاسبات به این شکل همراه با set -e احتیاط کنید. اگر نتیجه‌ی عبارت صفر شود، وضعیت خروج غیرصفر می‌شود و Bash خاتمه می‌یابد.

    x=0
    (( x++ ))
    echo "you will see this"
    
    set -e
    x=0
    (( x++ ))
    echo "shell exits on the previous line; you won't see this"
    

    برای بحث مفصل‌تر درباره‌ی عملگر ++، این مقاله را ببینید: پس‌افزایش در برابر پیش‌افزایش

جاهای دیگری که زمینه‌ی محاسباتی دارند

چند جای دیگر هم وجود دارد که زمینه‌ی محاسباتی دارند:

  • حلقه‌ی For محاسباتی.

    for (( i = 0; i < 10; i++ )); do
        echo "$i"
    done
    
  • بخش ایندکس یک آرایه‌ی دارای ایندکس عددی.

    echo "The next element is ${array[x + 1]}"
    # ................................^^^^^
    

    برای اطلاعات بیشتر درباره‌ی آرایه، صفحه‌ی مفهوم آرایه را بخوانید.

  • بخش‌های offset و length از بسط ${var:offset:length}.

  • دستور توکار let.

    • توجه کنید که (( expression )) به let "expresson" ترجیح داده می‌شود. برخلاف ساختار شرط محاسباتی، عبارت let در معرض بسط نام فایل و شکستن واژه قرار می‌گیرد و به نقل‌قول نیاز دارد.
  • تخصیص متغیر، زمانی که متغیر «ویژگی عدد صحیح» داشته باشد.

    # variable x does not have the integer attribute
    x="3 + 4"
    declare -p x      # => declare -- x="3 + 4"
    
    # variable y has the integer attribute
    declare -i y
    y="4 + 5"
    declare -p y      # => declare -i y="9"
    
    Note

    به عقیده‌ی نویسنده، declare -i کاربرد محدودی دارد. مزیت اصلی آن این است که اجازه می‌دهد محاسبات را «خام» انجام دهید (بدون نگارش عبارت محاسباتی). این می‌تواند برای خوانندگان کد شما گیج‌کننده باشد، چون عبارت‌های محاسباتی در جاهای غیرمنتظره ارزیابی می‌شوند.

استفاده از متغیرها در عبارت محاسباتی

اینجا یکی از جنبه‌های زمینه‌های محاسباتی را می‌بینید که خوانایی را بهبود می‌دهد.

height=10
width=20
area=$(( height * width ))

آیا متوجه شدید که علامت‌های دلار $ از متغیرهای داخل عبارت محاسباتی حذف شده‌اند؟ Bash به شما اجازه می‌دهد این کار را بکنید. این کار خواندن عبارت‌ها را بسیار آسان‌تر می‌کند.

راهنما می‌گوید:

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

حتی برای عناصر آرایه هم کار می‌کند:

point1=(10 15)  # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))

اما سایر عبارت‌های پارامتری باید از نگارش ویژه‌ی خود استفاده کنند. مثلاً دو برابر کردن طول یک رشته:

s="hello world"
len=$(( ${#s} * 2 ))

و پارامترهای موقعیتی هم به همین شکل باید از نگارش ویژه‌ی خود استفاده کنند. اگر دو پارامتر موقعیتی اول نماینده‌ی عرض و ارتفاع باشند:

area=$(( $1 * $2 ))
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود