Advanced Bash-Scripting Guide: Искусство программирования на языке сценариев командной оболочки | ||
---|---|---|
Назад | Вперед |
Инициализация переменной или изменение ее значения
Универсальный оператор присваивания, пригоден как для сравнения целых чисел, так и для сравнения строк.
var=27 category=minerals # Пробелы до и после оператора "=" -- недопустимы.
Пусть вас не смущает, что оператор присваивания ("="), по своему внешнему виду, совпадает с оператором сравнения (=). # Здесь знак "=" выступает в качестве оператора сравнения if [ "$string1" = "$string2" ] # if [ "X$string1" = "X$string2" ] более отказоустойчивый вариант, # предохраняет от "сваливания" по ошибке в случае, когда одна из переменных пуста. # (добавленные символы "X" компенсируют друг друга.) then command fi |
сложение
вычитание
умножение
деление
возведение в степень
# В Bash, начиная с версии 2.02, был введен оператор возведения в степень -- "**". let "z=5**3" echo "z = $z" # z = 125
модуль (деление по модулю), возвращает остаток от деления
bash$ echo `expr 5 % 3` 2
Этот оператор может применяться в алгоритмах генерации псевдослучайных чисел в заданном диапазоне (см. Пример 9-23 и Пример 9-25), для форматирования вывода на экран (см. Пример 25-10 и Пример A-7), и даже для генерации простых чисел (см. Пример A-18). На удивление часто операцию деления по модулю можно встретить в различных численных алгоритмах.
Пример 8-1. Наибольший общий делитель
#!/bin/bash # gcd.sh: поиск наибольшего общего делителя # по алгоритму Эвклида # Под "наибольшим общим делителем" (нод) двух целых чисел #+ понимается наибольшее целое число, которое делит оба делимых без остатка. # Алгоритм Эвклида выполняет последовательное деление. # В каждом цикле, #+ делимое <--- делитель #+ делитель <--- остаток #+ до тех пор, пока остаток не станет равным нулю (остаток = 0). #+ The gcd = dividend, on the final pass. # # Замечательное описание алгоритма Эвклида можно найти # на сайте Jim Loy, http://www.jimloy.com/number/euclids.htm. # ------------------------------------------------------ # Проверка входных параметров ARGS=2 E_BADARGS=65 if [ $# -ne "$ARGS" ] then echo "Порядок использования: `basename $0` первое-число второе-число" exit $E_BADARGS fi # ------------------------------------------------------ gcd () { # Начальное присваивание. dividend=$1 # В сущности, не имеет значения divisor=$2 #+ какой из них больше. # Почему? remainder=1 # Если переменные неинициализировать, #+ то работа сценария будет прервана по ошибке #+ в первом же цикле. until [ "$remainder" -eq 0 ] do let "remainder = $dividend % $divisor" dividend=$divisor # Повторить цикл с новыми исходными данными divisor=$remainder done # алгоритм Эвклида } # последнее $dividend и есть нод. gcd $1 $2 echo; echo "НОД чисел $1 и $2 = $dividend"; echo # Упражнение : # -------- # Вставьте дополнительную проверку входных аргументов, #+ и предусмотрите завершение работы сценария с сообщением об ошибке, если #+ входные аргументы не являются целыми числами. exit 0
"плюс-равно" (увеличивает значение переменной на заданное число)
let "var += 5" значение переменной var будет увеличено на 5.
"минус-равно" (уменьшение значения переменной на заданное число)
"умножить-равно" (умножить значение переменной на заданное число, результат записать в переменную)
let "var *= 4" значение переменной var будет увеличено в 4 раза.
"слэш-равно" (уменьшение значения переменной в заданное число раз)
"процент-равно" (найти остаток от деления значения переменной на заданное число, результат записать в переменную)
Арифметические операторы очень часто используются совместно с командами expr и let.
Пример 8-2. Арифметические операции
#!/bin/bash # От 1 до 6 пятью различными способами. n=1; echo -n "$n " let "n = $n + 1" # let "n = n + 1" тоже допустимо echo -n "$n " : $((n = $n + 1)) # оператор ":" обязателен, поскольку в противном случае, Bash будет #+ интерпретировать выражение "$((n = $n + 1))" как команду. echo -n "$n " n=$(($n + 1)) echo -n "$n " : $[ n = $n + 1 ] # оператор ":" обязателен, поскольку в противном случае, Bash будет #+ интерпретировать выражение "$[ n = $n + 1 ]" как команду. # Не вызывает ошибки даже если "n" содержит строку. echo -n "$n " n=$[ $n + 1 ] # Не вызывает ошибки даже если "n" содержит строку. #* Старайтесь избегать употребления такой конструкции, #+ поскольку она уже давно устарела и не переносима. echo -n "$n "; echo # Спасибо Stephane Chazelas. exit 0
Целые числа в Bash фактически являются знаковыми длинными целыми (32-бит), с диапазоном изменений от -2147483648 до 2147483647. Если в результате какой либо операции эти пределы будут превышены, то результат получится ошибочным. a=2147483646 echo "a = $a" # a = 2147483646 let "a+=1" # Увеличить "a" на 1. echo "a = $a" # a = 2147483647 let "a+=1" # увеличить "a" еще раз, с выходом за границы диапазона. echo "a = $a" # a = -2147483648 # ОШИБКА! (выход за границы диапазона) |
Bash ничего не знает о существовании чисел с плавающей запятой. Такие числа, из-за наличия символа десятичной точки, он воспринимает как строки. a=1.5 let "b = $a + 1.3" # Ошибка. # t2.sh: let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3") echo "b = $b" # b=1Для работы с числами с плавающей запятой в сценариях можно использовать утилиту-калькулятор bc. |
битовые операции. Битовые операции очень редко используются в сценариях командного интерпретатора. Их главное назначение, на мой взгляд, установка и проверка некоторых значений, читаемых из портов ввода-вывода и сокетов. "Битовые операции" гораздо более уместны в компилирующих языках программирования, таких как C и C++.
сдвигает на 1 бит влево (умножение на 2)
"сдвиг-влево-равно"
let "var <<= 2" значение переменной var сдвигается влево на 2 бита (умножается на 4)
сдвиг вправо на 1 бит (деление на 2)
"сдвиг-вправо-равно" (имеет смысл обратный <<=)
по-битовое И (AND)
"по-битовое И-равно"
по-битовое ИЛИ (OR)
"по-битовое ИЛИ-равно"
по-битовая инверсия
По-битовое отрицание
по-битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR)
"по-битовое ИСКЛЮЧАЮЩЕЕ-ИЛИ-равно"
логическое И (and)
if [ $condition1 ] && [ $condition2 ] # То же самое, что: if [ $condition1 -a $condition2 ] # Возвращает true если оба операнда condition1 и condition2 истинны... if [[ $condition1 && $condition2 ]] # То же верно # Обратите внимание: оператор && не должен использоваться внутри [ ... ].
оператор &&, в зависимости от контекста, может так же использоваться в И-списках для построения составных команд. |
логическое ИЛИ (or)
if [ $condition1 ] || [ $condition2 ] # То же самое, что: if [ $condition1 -o $condition2 ] # Возвращает true если хотя бы один из операндов истинен... if [[ $condition1 || $condition2 ]] # Also works. # Обратите внимание: оператор || не должен использоваться внутри [ ... ].
Bash производит проверку кода возврата КАЖДОГО из операндов в логических выражениях. |
Пример 8-3. Построение сложных условий, использующих && и ||
#!/bin/bash a=24 b=47 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] then echo "Первая проверка прошла успешно." else echo "Первая проверка не прошла." fi # ОКА: if [ "$a" -eq 24 && "$b" -eq 47 ] # пытается выполнить ' [ "$a" -eq 24 ' # и терпит неудачу наткнувшись на ']'. # # if [[ $a -eq 24 && $b -eq 24 ]] это правильный вариант # (в строке 17 оператор "&&" имеет иной смысл, нежели в строке 6.) # Спасибо Stephane Chazelas. if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] then echo "Вторая проверка прошла успешно." else echo "Вторая проверка не прошла." fi # Опции -a и -o предоставляют #+ альтернативный механизм проверки условий. # Спасибо Patrick Callahan. if [ "$a" -eq 24 -a "$b" -eq 47 ] then echo "Третья проверка прошла успешно." else echo "Третья проверка не прошла." fi if [ "$a" -eq 98 -o "$b" -eq 47 ] then echo "Четвертая проверка прошла успешно." else echo "Четвертая проверка не прошла." fi a=rhino b=crocodile if [ "$a" = rhino ] && [ "$b" = crocodile ] then echo "Пятая проверка прошла успешно." else echo "Пятая проверка не прошла." fi exit 0
Операторы && и || могут использоваться и в арифметических вычислениях.
bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 1 0 1 0
запятая
С помощью оператора запятая можно связать несколько арифметических в одну последовательность. При разборе таких последовательностей, командный интерпретатор вычисляет все выражения (которые могут иметь побочные эффекты) в последовательности и возвращает результат последнего.
let "t1 = ((5 + 3, 7 - 1, 15 - 4))" echo "t1 = $t1" # t1 = 11 let "t2 = ((a = 9, 15 / 3))" # Выполняется присваивание "a" = 9, #+ а затем вычисляется "t2". echo "t2 = $t2 a = $a" # t2 = 5 a = 9
Оператор запятая чаще всего находит применение в циклах for. См. Пример 10-12.