Арифметические операции и массивы

Сложные структуры данных и вычисления нельзя отнести к сильной стороне оболочки bash, но применяются работа с массивами и арифметическими операциями.

Все bash-переменные представляют собой строковые значения, поэтому оболочка bash не делает различия в присваиваниях между числом 1 и символьной строкой "1". Различие лежит в использовании переменных. Следующий код иллюстрирует это различие:

#!/bin/bash
а=1
b=$((2))
с=+$b
d=$(($a+$b))
echo " + $b =  \t(знак плюс как строковый литерал)"
echo " + $b = $d \t(знак плюс как арифметическое сложение)"

При выполнении этого сценария получится такой результат:

1 + 2 = 1+2 (знак плюс как строковый литерал)
1 + 2 = 3 (знак плюс как арифметическое сложение)

Следует обратить внимание на то, что знак "плюс" в присваивании переменной $с не работает как оператор конкатенации для строк. Он остается всего лишь литеральным символом, и посему эта строка эквивалентна следующей:

с="+$b"

Для того чтобы добиться вычисления, необходимо заключить выражение в двойные скобки $((...)), как показано выше в присваивании переменной $d. Но даже эта мера предосторожности не позволяет получить в переменной $d числового значения; это значение по-прежнему хранится в виде строки "3".

В оболочке bash реализован обычный ассортимент операторов: арифметических, логических и отношений (подробнее см. соответствующие man-страницы).

Массивы в командной оболочке bash могут показаться немного странными объектами и используются не очень часто, тем не менее при необходимости их можно применять. Литеральные массивы ограничиваются круглыми скобками, а отдельные элементы разделяются пробельными символами. Для включения литеральных пробелов в элемент можно использовать кавычки:

example=(aa 'bb сс' dd)

Для доступа к отдельным элементам массива используют выражение ${имя_ массива индекс}. Индексация начинается с нуля. Такие индексы, как "*" и "@", относятся к массиву в целом, а специальные конструкции ${#имя_массива * } и ${#имя_массива @ } возвращают количество элементов в массиве. Не следует путать эти выражения с конструкцией ${#имя_массива} хотя эта форма кажется более логичной, но в действительности она содержит указатель на первый элемент массива (эквивалент для ${#имя_массива0}).

Можно подумать, что выражение $example1 должно служить однозначной ссылкой на второй элемент массива, но оболочка bash интерпретирует эту строку так: $example (обозначение ссылки на $example0) плюс литеральная строка 1. Отсюда вывод: ссылаясь на переменные массива, всегда используют фигурные скобки (без каких-либо исключений).

В качестве примера можно рассмотреть сценарий, который иллюстрирует некоторые особенности bash-мacсивов и подводные камни, на которые можно наткнуться при управлении ими.

#!/bin/bash
example=(aa 'bb сс' dd)
example[3]=ее
echo "example[@] = ${example[0]}"
echo "Массив example содержит ${#example[@]} элементов"
for elt in "${example[8] }"; do
echo " Элемент = $elt"
done

Вот как выглядит результат выполнения этого сценария.

$ sh arrays
example [@] = аа bb сс dd ее
Массив example содержит 4 элемента
Элемент = аа
Элемент = bb сс
Элемент = dd
Элемент = ee

Данный пример правильно реализован и по этому выглядит не сложным к выполнению. Однако, сценарий с заменой for-строки вариантом:

for elt in ${example[@]}; do

(т.е. без кавычек, в которых заключено выражение массива) также работает, но вместо четырех элементов он выведет пять: аа, bb, сс, dd и ее.

Важно помнить, что все bash-переменные все равно остаются строками, поэтому работа с массивами в некотором роде иллюзия.