Треки
/
Bash
Bash
/
Салабус
/
Арифметика
Ар

Арифметика у Bash

{one: "1 вправа", few: "%{count} вправи", many: "%{count} вправ", other: "%{count} вправи"}

Про концепцію Арифметика

Bash уміє виконувати арифметичні дії, але лише з цілими числами. Якщо потрібні обчислення з плаваючою комою, нам доведеться звернутися до якоїсь зовнішньої програми (наприклад, awk або bc).

Арифметичні оператори перелічено (у порядку пріоритету) в посібнику: Арифметика оболонки.

Крім того, Bash виконує арифметичні дії лише в певних місцях (які називають арифметичними «контекстами»).

Арифметичне розгортання

Арифметичне розгортання має такий самий вигляд, як підстановка команд, але з подвійними дужками.

$(( expression ))

Цей синтаксис розгортається в результат обчислення виразу. Його часто використовують, щоб зберегти результат у змінній:

x=$(( 3 + 4 ))
Note

Скільки пробілів ставити всередині дужок - справа смаку. Це питання стилю та читабельності.

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

Автор, прихильний до читача, любить додавати пробіли, щоб арифметичний вираз було краще видно в дужках.

Арифметична умовна конструкція

Арифметичну умовну конструкцію виконують у подвійних дужках без провідного $:

(( expression ))

Вона не розгортається в результат. Натомість вона формує статус виходу.

  • Статус виходу дорівнює 0 (успіх), якщо результат виразу ненульовий.
  • Статус виходу дорівнює 1 (невдача), якщо результат виразу нуль.
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"
    

    Докладніше про оператор ++ розповідає стаття Postincrement vs Preincrement

Інші місця з арифметичним контекстом

Є ще кілька місць, де діє арифметичний контекст:

  • Арифметичний цикл for.

    for (( i = 0; i < 10; i++ )); do
        echo "$i"
    done
    
  • Частина з індексом у масиві з числовими індексами.

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

    Докладніше про масиви читайте в документі концепції Масиви.

  • Частини зі зсувом і довжиною в розширенні ${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 ))

Але інші вирази з параметрами потребують свого спеціального синтаксису. Наприклад, подвоєння довжини рядка тексту (англ. string):

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

Те саме стосується й позиційних параметрів. Якщо перші два позиційні параметри задають ширину і висоту:

area=$(( $1 * $2 ))
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці