Skip to content

Выражения и арифметика в bash

Bash поддерживает ТОЛЬКО целочисленные выражения. Если нужно выполнить float выражения, можно использовать сторонние утилиты. Как один из вариантов, сделать скрипт fp с таким содержанием:

Bash
#!/usr/bin/env bash
# fp - реализация операций с плавающей точкой через awk
# usage: fp "выражение"
awk "BEGIN { print $* }"

Арифметика в двойных круглых скобках (( ))

В двойных круглых скобках (()) выполняются арифметические вычисления. Внутри (()) не требуются $ перед именами переменных. Их можно встретить в цикле for или в if:

Bash
for ((i=0; i<size; i++)); do :; done
if (( max - 3 > x * 4 )); then :; fi

Результат выражения можно присвоить переменной, добавив $:

Bash
max=$(( intro + body + outro - 1 ))
median_loc=$((len / 2))

В операторе if $ не нужен, т.к. там для принятия решения достаточно булевых значений. Если при вычислении выражения в двойных круглых скобках (без $) получается ненулевой результат --> возвращается статус 0 (true), иначе - статус 1 (false).
(()) без $ интерпретируются как выполнение одной/нескольких команд, которые не возвращают результат вычислений. Хоть и есть исключения:

Bash
# Bash поддерживает некоторые C-like операторы присваивания
(( step ++ ))
(( median_loc = len / 2 ))
(( dist *= 4 ))

Выражения выше всё равно не возвращают ничего (кроме кода возврата в \$?), а только присваивают значение переменным. Их можно оформить и так:

Bash
step=$(( step ++ ))
median_loc=$(( median_loc = len / 2 ))
dist=$(( dist *= 4 ))

Не юзай $((step++)) как самостоятельную инструкцию в отдельной строке

Такие выражения опасно юзать как самостоятельные инструкции, ибо они будут возвращать числа (из-за $), которые интерпретатор воспримет как имя какой-то команды: если, например, выражение вернет 3 --> оболочка попытается запустить команду с именем 3.

![[Переменные в bash#^e5ea05]]

Однако внутри круглых скобок пробелы допускаются, ибо круглые скобки определяют границы этого "слова".

Пару слов о let

Раньше вместо (()) использовали (и используют) встроенную команду let:

Bash
# Эквивалентные выражения
(( step ++ ))
let "step++"

(( median_loc = len / 2 ))
let "median_loc = len / 2"

(( dist *= 4 ))
let "median_loc = len / 2"

Выражение let бери в кавычки

let принимает только одно "слово", а пробелы в выражении разделяют выражение на несколько слов (чревато синтаксической ошибкой), и чтобы не было ошибок --> арифметическое выражение заключай в кавычки.


"Голая" арифметика

Есть один не очень желательный способ выполнения арифметических вычислений без использования (()) или let --> суть в объявлении заранее переменной как integer, после которого можно будет с переменной выполнять арифметические вычисления без (()) или let:

Bash
declare -i SEE
X=9
Y=3

SEE=X+Y    # 12  (интерпретируется как АРИФМЕТИЧЕСКАЯ операция)
SAW=X+Y    # X+Y (интерпретируется как СТРОКОВЫЙ литерал)
SUU=$X+$Y  # 9+3 (интерпретируется как КОНКАТЕНАЦИЯ строк)

Такой стиль выполнения арифметических действий чреват ошибками, ибо:
1) Можно просто забыть объявить переменную как целочисленную
2) Оболочка не сообщит об "ошибке"
3) Не очень очевидно в коде будет, какая из переменных integer


Соус: Книга "Идиомы Bash" --> Глава 5. "Выражения и арифметика"

bash