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

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

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

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

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

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

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

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

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

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

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

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

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

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

Пробелы не нужны

В операции присваивания пробелы вокруг ”=” не допускаются, ибо синтаксически вся конструкция присваивания должна быть одним “словом”.

Link to original

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

Пару слов о let

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

# Эквивалентные выражения
(( step ++ ))
let "step++"
 
(( median_loc = len / 2 ))
let "median_loc = len / 2"
 
(( dist *= 4 ))
let "median_loc = len / 2"

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

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


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

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

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