[me@linuxbox ~]$ echo $((2#11111111))
255
В этих примерах выводится значение шестнадцатеричного числа ff (наибольшее двухзначное число) и наибольшее восьмизначное двоичное число (в системе счисления с основанием 2).
Унарные операторы
Оболочка поддерживает два унарных оператора, + и -, используемых для обозначения положительных и отрицательных чисел соответственно.
Простая арифметика
В табл. 34.2 перечислены обычные арифметические операторы.
Таблица 34.2. Арифметические операторы
Оператор
Описание
+
Сложение
-
Вычитание
*
Умножение
/
Целочисленное деление
**
Степень числа
%
Деление по модулю (остаток от целочисленного деления)
Действия большинства из перечисленных операторов не вызывают вопросов, кроме целочисленного деления и деления по модулю, которые требуют дополнительного обсуждения.
Поскольку оболочка поддерживает только арифметические операции с целыми числами, результатом деления всегда будет целое число:
[me@linuxbox ~]$ echo $(( 5 / 2 ))
2
Это обстоятельство увеличивает важность операции определения остатка от деления:
[me@linuxbox ~]$ echo $(( 5 % 2 ))
1
Используя операторы деления и деления по модулю, можно определить, что деление 5 на 2 дает в результате 2 с остатком 1.
Вычисление остатка от деления удобно использовать в циклах. Это позволяет выполнять в цикле определенные операции с заданным интервалом. В примере ниже выводится строка чисел, в которой выделяются числа, кратные 5:
#!/bin/bash
# modulo : демонстрация оператора деления по модулю
for ((i = 0; i <= 20; i = i + 1)); do
remainder=$((i % 5))
if (( remainder == 0 )); then
printf "<%d> " $i
else
printf "%d " $i
fi
done
printf "\n"
Запустив этот сценарий, вы получите следующий результат:
[me@linuxbox ~]$ modulo
<0> 1 2 3 4 <5> 6 7 8 9 <10> 11 12 13 14 <15> 16 17 18 19 <20>
Присваивание
Хотя на данном этапе это не очевидно, тем не менее арифметические выражения могут выполнять операцию присваивания. Мы уже выполняли присваивание много раз, хотя и в других контекстах. Каждый раз, передавая переменной число, мы выполняем присваивание. То же самое можно делать в арифметических выражениях:
[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo $foo
[me@linuxbox ~]$ if (( foo = 5 ));then echo "It is true."; fi
It is true.
[me@linuxbox ~]$ echo $foo
5
В примере выше мы сначала присвоили переменной foo пустое значение и проверили, что она действительно получила пустое значение. Далее выполнили команду if с составной командой (( foo = 5 )). Эта команда имеет два интересных аспекта: (1) она присваивает значение 5 переменной foo и (2) оценивает ее значение как истинное, потому что присваивание прошло успешно.
ПРИМЕЧАНИЕ
Важно запомнить значение оператора = в примере выше. Одиночный знак = выполняет присваивание: выражение foo = 5 говорит: «Сделать значение переменной foo равным 5». Двойной знак == определяет эквивалентность: выражение foo == 5 говорит: «Переменная foo равна 5?» Это обстоятельство может вызывать путаницу, потому что команда test интерпретирует одиночный знак = как оператор сравнения строк. Это еще одна причина предпочесть более современные составные команды [[ ]] и (( )) вместо test.
В дополнение к оператору = командная оболочка поддерживает еще несколько очень полезных операторов присваивания, перечисленных в табл. 34.3.
Таблица 34.3. Операторы присваивания
Форма записи
Описание
параметр = значение
Простое присваивание. Присваивает указанное значение указанному параметру
параметр += значение
Присваивание со сложением. Эквивалентно выражению параметр = параметр + значение
параметр -= значение
Присваивание с вычитанием. Эквивалентно выражению параметр = параметр - значение
параметр *= значение
Присваивание с умножением. Эквивалентно выражению параметр = параметр × значение
параметр /= значение
Присваивание с целочисленным делением. Эквивалентно выражению параметр = параметр ÷ значение
параметр %= значение
Присваивание с делением по модулю. Эквивалентно выражению параметр = параметр % значение
параметр++
Постинкремент переменной. Эквивалентно выражению параметр = параметр + 1. (Но см. обсуждение ниже.)
параметр--
Постдекремент переменной. Эквивалентно выражению параметр = параметр - 1
++параметр
Преинкремент переменной. Эквивалентно выражению параметр = параметр + 1
--параметр
Предекремент переменной. Эквивалентно выражению параметр = параметр - 1
Эти операторы присваивания обеспечивают удобный и компактный способ записи многих арифметических вычислений. Особый интерес представляют операторы инкремента (++) и декремента (--), они увеличивают или уменьшают значение своего параметра на 1. Эти операторы заимствованы из языка программирования C и внедрены в несколько других языков программирования, включая bash.
Операторы инкремента и декремента могут находиться перед параметром или после него. Хотя в обоих случаях они увеличивают или уменьшают значение параметра на 1, тем не менее их местоположение играет важную роль. Если оператор помещается перед параметром, сначала выполняется операция инкремента (или декремента) и только потом возвращается измененное значение параметра. Если оператор помещается за параметром, операция выполняется после возврата значения. Такое поведение может показаться странным, но оно реализовано с умыслом. Взгляните на следующий пример:
[me@linuxbox ~]$ foo=1
[me@linuxbox ~]$ echo $((foo++))
1
[me@linuxbox ~]$ echo $foo
2
Если присвоить переменной foo значение 1 и затем увеличить ее значение с помощью оператора ++, следующего за именем переменной, выражение вернет прежнее значение 1 переменной foo. Однако если вывести значение переменной второй раз, мы увидим увеличенное значение. Если поместить оператор ++ перед параметром, мы получим более ожидаемый результат:
[me@linuxbox ~]$ foo=1
[me@linuxbox ~]$ echo $((++foo))
2
[me@linuxbox ~]$ echo $foo
2
Для большинства приложений на языке командной оболочки более полезным будет префиксный оператор.
Операторы ++ и -- часто используются совместно с циклами. Внесем некоторые улучшения в сценарий, демонстрирующий применение оператора деления по модулю, чтобы немного сократить его:
#!/bin/bash
# modulo2 : демонстрация оператора деления по модулю
for ((i = 0; i <= 20; ++i )); do
if (((i % 5) == 0 )); then
printf "<%d> " $i
else
printf "%d " $i
fi
done
printf "\n"
Битовые операции
Командной оболочкой поддерживается класс операторов, которые манипулируют числами не совсем обычным способом. Эти операторы действуют на уровне битов. Они применяются для выполнения некоторых низкоуровневых операций, часто связанных с установкой или чтением битовых флагов. Описание битовых операторов приводится в табл. 34.4.
Таблица 34.4. Битовые операторы
Оператор
Описание
~
Поразрядное отрицание. Изменяет значения всех битов в числе на противоположные
<<