Поблагодари автора прямо сейчас на странице Спасибо!

Программирование на bash

Материал из Пингвиньи радостей

Перейти к: навигация, поиск

Статья об основных элементах программирования на bash

Программирование на bash это процесс создания программ с использованием интерпретатора командной строки *nix систем Bash.
Исполняемый (интерпретируемый) код располагается в файлах обычно называемых Bash-скриптами.

Содержание


Структура скрипта

Простота не предшествует сложности, а вытекает из нее. (Афоризмы программирования)

Скрипт написанный на Bash

  • начинается со строки #!/bin/bash.
  • может принимать параметры и содержать код возврата
  • состоит из следующих основных элементов: Переменные, Циклы, Ветвления, Функции, Команды
  • команды могут объединяться в группы или формироваться списком с помощью Скобок
  • может включать куски кода из внешнего файла
  • строка начинающаяся со знака # не обрабатывается и может содержать комментарии
  • заканчивается командой exit или когда закончатся операторы

Код возврата

Аксиома: В любой программе есть ошибки. (Теория ошибок. Юмор программистов.)

Код возврата это символ(ы) устанавливающий статус выполнения программного кода скрипта (или функции). Код возврата позволяет вызывающей программе (алгоритму скрипта) организовать ветвление.

Код возврата скрипта может быть установлен командами выполненными последними

  # true                   - установка кода возврата = 0
  # false                  - установка кода возврата = 1
  # exit <КОД ВОЗРАТА>     - установка определённого кода возврата
    где  <КОД ВОЗРАТА>       это число в диапазоне 0 - 255

Если команда exit не содержит операторов или её вовсе нет, то код возврата будет такой какой устанавливает последняя выполненная команда скрипта.
Для анализа кода возврата в вызывающей программе используется переменная $?.

  Замечание:
  В силу того, что Код возврата представляет собой строковую переменную, возврат результата работы
  скрипта или функции может быть организован и в виде любой строковой последовательности.
  Например для получения многобайтовой последовательности в качестве результата работы функции:
     # function sample() { echo "string"; }; echo "return function = $( sample )"

Ссылки:

Подключение внешнего кода

Гений подобен холму, возвышающемуся на равнине. (Козьма Прутков).

В скрипты Bash могут подключиться куски кода из внешнего файла, что позволяет делать их бесконечно сложными. Для этого используется команда . или слово source, представляющая аналог #include.

Некоторые примеры использования

  source file                        - подключение кода Bash из файла
  ls | sed ... | . /dev/stdin        - подключение кода (полученного путём выполнения предыдущей команды) из стандартного входа

Ссылки:

Переменные

То, что для одного человека константа, для другого - переменная. (Законы Мерфи)

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

Для явного (простого) присвоения переменной используется конструкция

   a=abc
   a=${a}'123'			# a=abc123
   Внимание! в данной конструкции в левой части присвоения не может быть использована переменная.

Для неявного (например как ссылка на другую переменную) определения переменной используется команда eval

   x=a
   eval "$x=abc"
   eval "$x=\${$x}'123'"

В некоторых случаях может оказаться удобней определение переменной через команду printf

   printf [-v переменная] формат [аргументы]
   например для Пересчета секунд в ЧЧ:ММ:СС
   s=1000; printf -v t '%dч %02dм %02dс' `expr $s / 3600` `expr $s / 60 % 60` `expr $s % 60` ; echo $t

В BASH могут быть определены одномерные массивы - переменные специального вида, доступ к которым осуществляется с одним именем, но разными индексами.
Для управления массивами переменных используются конструкции

  name[n]=value                - задание n-го элемента массива <name>
  name==( val1 val2 ... )      - инициализация всего массива <name>
  name[${x}]="string"          - задание элемента массива <name> с индексом из переменной ${x}
  echo ${name[n]}              - доступ к n-му элементу массива
  unset name[n]                - освобождение n-го элемента массива

Объявляемые переменные могут быть глобальными - видимыми во всём теле скрипте и локальными - видимыми только в функции или в блоке кода заключённом в [ ]. По умолчанию переменные используемые в Bash определяются как глобальные. В целях приведения логики программирования скриптов к обычно используемой в языках программирования, полезно при использовании переменных нужных только в функциях всегда объявлять их как локальные.
Для объявления переменной в качестве локальной используется конструкция

  local x=a

Для определения переменных с наложением ограничений на их тип используются инструкции declare и typeset, которые являются синонимами. Попытка действий с переменными не соответствующая объявленному типу завершается сообщением об ошибке.
Для определения переменных с контролем типа

  # declare -r var              - объявление переменной var только для чтения (аналог константы)
  # declare -i number           - объявление переменной number как целое число
  # declare -a indices          - объявление переменной indices как массива
  # declare -f functions        - выводит имя функции function_name, если она была объявлена ранее
  # declare -x var_out          - объявление переменной var_out как доступной для экспорта
  # declare -x var=$value       - объявление переменной var и присваивания значения ей одновременно
  # declare -f                  - (без аргументов) выводит список ранее объявленных функций в сценарии

Дочерний процесс запущенный из скрипта определяет/инициализирует собственный набор внутренних переменных. В некоторых случаях требуется передать переменные из родительского дочернему процессу. Например при инициализации системы для передачи переменных окружения пользовательским процессам.
Для экспорта переменных используется команда export.

  # export var=(a b); echo ${var[0]}
  # var=(a b); export var; echo ${var[0]}
    a

Для удаления (фактически осуществляется установка ее значения в null) переменной используется команда unset.

  # unset var                   - сброс переменной $var

Ссылки:

Специальные и внутренние переменные

Того, чего нужно, того как раз нет, а когда оно не нужно — его прям навалом. (Законы Мерфи)

При инициализации оболочки Bash определяет большой набор внутренних переменных.

Отдельный класс внутренних переменных представляют собой переменные имена которых формируются специальными символами:

  $0     - имя выполняемого скрипта
  $#     - количество позиционных параметров, переданных сценарию
  $_     - последний аргумент предыдущей из выполнявшихся команд
  $?     - код завершения последней команды (0 – успешно, другое – ошибка)
  $$     - PID текущего процесса (? следующий свободный PID)
  $!     - PID последнего асинхронного процесса
  $*     - все позиционные параметры, собранные в одну строку («$1x$2x…$n»)
  $@     - все позиционные параметры, подлежащие дальнейшему разбору («$1» «$2» …«$n»)
  здесь позиционным параметром называется строка(и) переданная скрипту в качестве параметра при запуске.
  Имя позиционного параметра в скрипте представляет собой натуральное число начиная с 1, например
  $1 $2 ... - первый, второй и так далее позиционный параметр

Некоторые примеры использования специальных переменных

# touch hello.$$            - создать файл с уникальным расширением
# touch $$hello             - создать файл с уникальным именем

Ссылки:

Циклы

Основным преимуществом компьютера перед человеком является очень быстрое выполнение повторяющихся действий. (Афоризм)

Существуют следующие типы циклов: ЦИКЛ ДЛЯ, ЦИКЛ ПОКА, ЦИКЛ ДО и ЦИКЛ ТРЁХ ВЫРАЖЕНИЙ.
Для досрочного выхода из цикла используется слово: break

Ссылки:

Циклы ДЛЯ

Циклы ДЛЯ используются для перебора конечных значений аргумента. Конструкция цикла

  for ПЕРЕМЕННАЯ in ВЫРАЖЕНИЕ; do
     ТЕЛО ЦИКЛА
  done

Некоторые примеры циклов

for VARIABLE in 1 2 3 4 5 .. N; do           # Конечный цикл с перебором значений
   echo "$VARIABLE"
done

for i in {0..10..2}; do                      # Конечный цикл с приростом аргумента
   #     {START..END..INCREMENT}             # только для bash version 3.0+ и выше ?
   echo "$i"
done

for file in /etc/*; do                       # Конечный цикл по содержимому каталога
   echo "${file}"
done

for str in $(cat $FILE); do                  # Конечный цикл по содержимому файла
   echo "${str}"
done

Циклы ПОКА

Циклы ПОКА используются для выполнения действий по счётчику. При этом тело цикла будет выполнено только в случае есть условие цикла ИСТИННО. Конструкция цикла

  while ВЫРАЖЕНИЕ; do
     ТЕЛО ЦИКЛА
  done

Некоторые примеры циклов

COUNTER=0
while [  $COUNTER -lt 10 ]; do           # конечный цикл по арифметическому счётчику COUNTER
   echo The counter is $COUNTER
   let COUNTER=COUNTER+1
done

while read line; do                      # чтение файла построчно
   echo "$line"
done < "test.data"

Циклы ДО

Циклы ДО используются для выполнения действий по счётчику. При этом тело цикла будет выполнено только если условие ЛОЖНО. Проверка условия будет осуществлена ДО выполнения тела цикла. Конструкция цикла

  until ВЫРАЖЕНИЕ; do
     ТЕЛО ЦИКЛА
  done

Некоторые примеры циклов

COUNTER=20
until [ $COUNTER -lt 10 ]; do                # Конечный цикл арифметическому счётчику COUNTER
   echo COUNTER $COUNTER
   let COUNTER-=1
done

Циклы ТРЁХ ВЫРАЖЕНИЙ

Циклы ТРЁХ ВЫРАЖЕНИЙ используют для работы три параметра-выражения: инициализатор аргумента, условие прекращения цикла и способ его прироста. Данный вид цикла похож на тот который применяется в языках программирования, например Си.
Конструкция цикла

  for (( EXP1; EXP2; EXP3 )); do
     ТЕЛО ЦИКЛА
  done

Некоторые примеры циклов

for (( c=1; c<=5; c++ )); do                # Конечный цикл с Си-образным видом
   #  где
   #  c=1;   инициализация переменной цикла
   #  c<=5;  условие выполнения цикла
   #  c++    правило изменения переменной цикла
   command
done

for (( ; ; )); do                            # Бесконечный цикл с Си-образным видом
   echo "infinite loops [ hit CTRL+C to stop]"
done

Ветвление

Условное поведение пожалуй основная отличительная особенность разумного поведения от неразумного (Мысли вслух).

Основные элементы Ветвлений это Ветвление ЕСЛИ и Ветвление ВЫБОР.

Ветвление ЕСЛИ

Ветвление ЕСЛИ используется для выбора одного из двух вариантов дальнейшего кода в зависимости от значения условия.
Конструкция ветвления

  if УСЛОВИЕ; then
     ВЫРАЖЕНИЕ_ИСТИНА        # выполняется. если УСЛОВИЕ возвращает ИСТИНА (значение 0)
  else
     ВЫРАЖЕНИЕ_ЛОЖЬ          # выполняется. если УСЛОВИЕ возвращает ЛОЖЬ (значение отличное от 0, например 1)
  fi
 где
 УСЛОВИЕ - любое выражение, возвращающее ИСТИНА или ЛОЖЬ.
 Например команда test ( конструкция [ ОПЕРАНДЫ ]), команда grep с ключом -q и другие.

Некоторые примеры ветвления ЕСЛИ

if [ -z "$found" ]; then
   echo "переменная не  существует"
else
   echo "переменная существует"
fi

Ветвление ВЫБОР

Ветвление ВЫБОР используется для выбора нескольких вариантов дальнейшего кода в зависимости от значения условия.
Конструкция ветвления

  case УСЛОВИЕ in
     ЗНАЧЕНИЕ_1)              # выполняется если УСЛОВИЕ возвращает ЗНАЧЕНИЕ_1
     ВЫРАЖЕНИЕ
     ;;
     ЗНАЧЕНИЕ_N)              # выполняется если УСЛОВИЕ возвращает ЗНАЧЕНИЕ_N
     ВЫРАЖЕНИЕ
     ;;
     *)                       # выполняется если УСЛОВИЕ не возвращает вышеприведённых значений
     ВЫРАЖЕНИЕ
     ;;
  esac

Функции

Мышление функциями признак хорошего тона (Мысли вслух).

В скрипте могут быть определены Функции - куски повторяемого кода. Функции делают скрипт более читаемым и уменьшают его размер. Функция может принимать параметры и содержать код возврата. Определения функций должны располагаться до начала их использования.
Конструкции функций

  function ИМЯ_ФУНКЦИИ {          # простая функция без параметров
  КОМАНДЫ
  }
  function ИМЯ_ФУНКЦИИ () {       # функции с параметрами и кодом возврата
  echo $1
  return КОД_ВОЗВРАТА
  }

Если параметров несколько перебор их можно осуществить командой shift.
Некоторые примеры функций

function sample() {                # пример функции с несколькими параметрами
  while [ ! -z "$1" ]; do          # пока параметры есть (длина параметра не равна нулю)
    shift                          # смещение по параметрам
    echo "$1"
  done
  return 0                         # выход с установкой кода возврата
}

Ссылки:

Скобки

Всякая вещь есть форма проявления беспредельного разнообразия. (Козьма Прутков).

Bash поддерживает Скобки : круглые ( ), фигурные { } и двойные квадратные [[ ]].

 Внимание!
 Конструкция с одинарными скобками [ ] на самом деле скобками не являются, представляет собой псевдоним встроенной команды test.
 Левая скобка [ это "необычная" программа из /usr/bin/[
 Правая кнопка ]  это просто аргумент для это программы, который предотвращает использование других аргументов - "закрывает" скобку.

Круглые скобки

Круглые скобки позволяют осуществить группировку команд, например при использовании их как одну в конвейерах. Запуск команд расположенных внутри круглых скобок осуществляется в отдельном подпроцессе.

Некоторые примеры

  # ls | ( dirs=$(cat - ); echo $dirs)

Ссылки:

Фигурные скобки

Фигурные скобки позволяют осуществить формирование групп строк разделенных пробелом одной командой. При использовании фигурных скобок происходит как бы их раскрытие, подстановка, преобразование конструкции в строку, по механизму похожему для команды xargs. По смыслу конструкция может рассматриваться как Псевдоним диапазона или списка.

Конструкции формирование списка путём перебора

 {строка1,строка2,...,строкаN}
 # echo {1,2}
   1 2
 [префикс]{........}[суффикс]
 # echo x{1,2}y
   x1y x2y

Конструкция формирование списка путём воссоздания последовательности

 {<начало>..<конец>}
 # echo {1..3}
   1 2 3
 # echo {a..e}
   a b c d e
 {<начало>..<конец>..<шаг>} (Bash 4 +)
 # echo {1..9..3}
   1 4 7
 # echo {a..e..2}
   a c e

Фигурные скобки могут быть вложенными

  # echo {{1,2},+,{a..c}}
    1 2 + a b c

Ссылки:

Двойные квадратные скобки

Двойные квадратные скобки это специальная конструкция Bash выполнения логических выражений, например в целях осуществления ветвлений.

Некоторые примеры

   [[ a < b ]]                             - лексикографическое сравнение, допустимо использование < >
   [[ a = a && b = b ]]                    - внутри может использоваться && и ||
   [[ (a = a || a = b) && a = b ]]         - внутри может использоваться ( )
   x='a b'; [[ $x = 'a b' ]]               - кавычки у имени переменной не нужны
   [[ ab = a? ]]                           - могут использоваться * ?
   [[ ab? =~ 'ab?' ]]                      - может использоваться расширенное регулярное выражение POSIX

Ссылки:

Разбор параметров скрипта

Человеку даны две руки на тот конец, дабы он, принимая левою, раздавал правою. (Козьма Прутков).

Использование ключей в командной строке делает использование скрипта более совершенным. Будем считать, что ключи (опции командной строки) это символы которым предшествует префиксы "минус" (-) или "плюс" (+). В этом случае разбор параметров командной строки (параметров скрипта с ключами) может быть удобно осуществлен с помощью внутренней команды bash getopts или внешней программы getopt.

Команда getopts обычно используется внутри цикла. С командой связаны внутренние переменные $OPTIND - указатель на аргумент и $OPTARG - дополнительный аргумент опции.
Пример использования getopts

while getopts ":dp:" Option; do
   echo $OPTIND;
    case $Option in
	d) echo "ключ -d-";;
	p) echo "ключ -p-, с аргументом \"$OPTARG\"";;
	*) echo "недопустимый ключ.";;
    esac
done

Пример использования программы getopt

set -- `getopt "abcd:" "$@"`   # разбор аргументов командной строки в позиционные параметры.
while [ ! -z "$1" ]
do
  case "$1" in
    -a) echo "Опция \"a\"";;
    -b) echo "Опция \"b\"";;
    -c) echo "Опция \"c\"";;
    -d) echo "Опция \"d\" $2";;
     *) break;;
  esac
  shift
done

Ссылки:

Организация диалога

Введите любое 11-значное простое число, чтобы продолжить... (Юмор программистов).

Создание диалога позволяет организовать общение скрипта с пользователем и придать тем самым ему вид настоящей программы.

Для создания диалога в скрипте используются конструкции

  • команда read
   echo "продолжать? (y/n): ";
   read yesno;                        # считывание строки (до ввода ENTER) в переменную yesno
   if [ $yesno = "y" -o $yesno = "Y" ]; then
      echo Yes
   else
      echo No
   fi
   echo "продолжать? (y/n): ";
   read -n 1 yesno;                   # посимвольное считывание ввода в переменную yesno
   if ! $( echo $yesno | grep -q "y"); then
      echo "скрипт завершается"
      exit 1
   fi
  • команда select
   select fname in *; do              # команда select организует простое меню и размещает ввод пользователя в $fname
      echo you picked $fname \($REPLY\)
      break;
   done

Для создания развитых диалоговых окон могут быть использованы

  • консольная утилита dialog
  • консольная утилита whiptail
  • графическая оконная программа zenity

Ссылки:

Форматирование вывода

Если хочешь быть красивым, поступи в гусары. (Козьма Прутков).

Форматирование вывода облегчает восприятие выводимой на экран информации, например при использовании скрипта. Для форматирования используются такие инструменты как управление курсором, цветом, шрифтом и экраном. В данном случае под управлением курсором понимается управление его местоположением на экране, под управлением цветом и шрифтом понимается управление формой (тонкий, толстый, мелькающий, подчёркнутый и реверсный) и цветом отображаемого текста, под управлением экраном понимается управление отображением/очисткой теста на экране и его перемещением. Здесь и далее экраном называется окно эмулятора терминала.

Для Форматирования вывода используются ESC-последовательности (спец)символов.
ESC-последовательности это такие последовательности байтов информации, которые начинаются с символа ESC (033) и интерпретируются эмулятором терминала специальным образом. Обычно для передачи ESC-последовательности эмулятору терминала применяются стандартные команды вывода информации на экран, например расширенный режим команды echo. Для устранения зависимости скрипта, форматирующего вывод на экран, от типа используемого терминала, может быть использована специальная утилита tput, передающая ESC-последовательности в виде их символьных кодов, а также утилита установки параметров терминала setterm из системного пакета util-linux.

Таблица (некоторых) ESC-последовательностей:

  # esc=$(echo -e "\E")                      - получение символа ESC (параметр -e устанавливает расширенный режим команды echo)
  # esc=$(echo -en "\033")                   - альтернативное получение символа ESC
  управление цветом текста                                                             смещение цвета для режима повышенной яркости (\E[1m)
  # printf ${esc}[30m                          - установка цвета текста ЧЁРНЫЙ               СЕРЫЙ
  # printf ${esc}[31m                          - установка цвета текста КРАСНЫЙ              РОЗОВЫЙ
  # printf ${esc}[32m                          - установка цвета текста ЗЕЛЁНЫЙ              САЛАТОВЫЙ
  # printf ${esc}[33m                          - установка цвета текста КОРИЧНЕВЫЙ           ЖЁЛТЫЙ
  # printf ${esc}[34m                          - установка цвета текста СИНИЙ                СВЕТЛО-СИНИЙ
  # printf ${esc}[35m                          - установка цвета текста ПУРПУРНЫЙ            ЛИЛОВЫЙ
  # printf ${esc}[36m                          - установка цвета текста БИРЮЗОВЫЙ            ГОЛУБОЙ
  # printf ${esc}[37m                          - установка цвета текста БЕЛЫЙ                ЯРКО-БЕЛЫЙ
  # printf ${esc}[38m                          - отмена установки цвета текста
  # printf ${esc}[39m                          - отмена установки цвета текста
  управление цветом фона текста                                                        смещение цвета для режима повышенной яркости (\E[1m)
  # printf ${esc}[40m                          - установка цвета фона текста ЧЁРНЫЙ          СЕРЫЙ
  # printf ${esc}[41m                          - установка цвета фона текста КРАСНЫЙ         РОЗОВЫЙ
  # printf ${esc}[42m                          - установка цвета фона текста ЗЕЛЁНЫЙ         САЛАТОВЫЙ
  # printf ${esc}[43m                          - установка цвета фона текста КОРИЧНЕВЫЙ      ЖЁЛТЫЙ
  # printf ${esc}[44m                          - установка цвета фона текста СИНИЙ           СВЕТЛО-СИНИЙ
  # printf ${esc}[45m                          - установка цвета фона текста ПУРПУРНЫЙ       ЛИЛОВЫЙ
  # printf ${esc}[46m                          - установка цвета фона текста БИРЮЗОВЫЙ       ГОЛУБОЙ
  # printf ${esc}[47m                          - установка цвета фона текста БЕЛЫЙ           ЯРКО-БЕЛЫЙ
  # printf ${esc}[48m                          - отмена установки цвета фона текста
  # printf ${esc}[49m                          - отмена установки цвета фона текста
  управление шрифтом
  # printf ${esc}[0m                           - сброс всех атрибутов
  # printf ${esc}[1m                           - установка режима яркий (утолщённый)
  # printf ${esc}[2m                           - установка режима тусклый (тонкий)
  # printf ${esc}[4m                           - установка режима подчеркнутый
  # printf ${esc}[5m                           - установка режима мигающий
  # printf ${esc}[7m                           - установка режима реверсный
  управление курсором
  # printf ${esc}[s                            - запомнить положение курсора
  # printf ${esc}[u                            - восстановить запомненное положение курсора
  # printf ${esc}[nA                           - вверх на n строк
  # printf ${esc}[nB                           - вниз на n строк
  # printf ${esc}[nC                           - вправо на n строк
  # printf ${esc}[nD                           - влево на n строк
  # printf ${esc}[nE                           - в начало строки и на n строк вниз 
  # printf ${esc}[nF                           - в начало строки и на n строк вверх 
  # printf ${esc}[n1;n2H                       - переместить в позицию n2 строки n1
  # printf ${esc}[nZ                           - на n табуляций назад
  # printf ${esc}[n`                           - в той же строке в позицию n
  # printf ${esc}[nd                           - в той же позиции в строку n
  # printf ${esc}M                             - сдвинуть курсор на строчку вверх, если он находится в самой верхней строке, то сдвинуть содержимое экрана на строчку вниз
  управление экраном
  # printf ${esc}c                             - очистить весь экран и установить курсор в левый верхний угол
  # printf ${esc}[0J                           - очистить от курсора до конца экрана
  # printf ${esc}[1J                           - очистить от начала экрана до курсора
  # printf ${esc}[2J                           - очистить весь экран
  # printf ${esc}[0K                           - очистить от курсора до конца строки
  # printf ${esc}[1K                           - очистить от начала строки до курсора
  # printf ${esc}[2K                           - очистить всю строку
  # printf ${esc}[nX                           - очистить очистить n знаков справа от позиции курсора 
  # printf ${esc}[nL                           - вставить n пустых строк ниже текущей строки
  # printf ${esc}[nM                           - удалить n строк ниже текущей строки
  # printf ${esc}[nP                           - удалить n знаков справа от курсора (в пределах строки)
  # printf ${esc}[n@                           - вставить n знаков справа от курсора (в пределах строки)

ESC-последовательности могут соединяться в одной строке через точку с запятой, например

  #  echo -e "\E[44;36;1mTest\E[0m"

Некоторые примеры применения форматирования вывода

  # esc=$(echo -e "\E"); echo "${esc}[31;5m Test ${esc}[0m"       -вывод текста красным мигающим шрифтом

Ссылки:

Секреты

Если тебе что-то не нравится, то не спеши это исправлять, если не помнишь, для чего это сделали. (Юмор программистов).

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

Для получения результата арифметической операции могут быть использованы

  • встроенные в bash функции
  • команда expr из пакета coreutils
  • возможност языка awk
  • отдельная программа-калькулятор concalc

Вычисление с помощью сторонних программ медленнее, но возможности их шире.

Примеры арифметических операций

  вычисления с помощью встроенной функции
  # echo $(( 100 / 3 ))                          - частное от деления
  # myvar="56"; echo $(( $myvar + 12 ))          - сложение
    В качестве операндов допустимы символы
    +      - сложение
    -      - вычитание
    *      - умножение
    /      - целочисленное деление
    %      - остаток от деления
  вычисления с помощью команды ядра
  # expr 2 "+" 3                                 - сложение
  # expr 2 "<" 3                                 - логическое сравнение
  вычисления с помощью awk
  # echo 2 | awk '{ print ($1^2)+1 }'            - степень числа
  вычисления с помощью concalc
  # concalc 2000398934016 / 1024 / 1024          - множественное деление чмсла
  # concalc sqrt 10                              - корень кватратный числа

Ссылки:

Перехват прерываний

Перехват прерываний расширяет возможности по управлению работой скрипта. Например позволяет контролировать ввод сигнала прекращения работы CTRL-C. Для осуществления перехвата используется команда trap.

Часто используемые команды и примеры перехватов

  # trap -l                 - получение списка доступных ловушек
     Внимание! при указании имени сигнала первые три буквы могут быть опущены.
  # trap action signal      - общий формат установки ловушки
    где
    action - инструкция (команда/функция скрипта) выполняемая при получении сигнала
    signal - сигнал, на который должна  быть вызвана инструкция
  # trap sorry INT          - пример вызова функции скрипта "sorry" при нажатии CTRL-C (SIGINT)
  # trap – INT              - отключение ловушки
  # trap ” INT              - "обнуление" ловушки, ничего не делать при получении сигнала

Ссылки:

Логическое И/ИЛИ

Для управления последовательностью выполнения нескольких выражений могут быть использованы операторы && (логическое И) и || (логическое ИЛИ).

Примеры использования

# выражение1 && выражение2          - выполнение выражение2 только если выражение1 вернуло ИСТИНА (0)
# выражение1 || выражение2          - выполнение выражение2 только если выражение1 вернуло ЛОЖЬ (не 0)

Ссылки:

Подстановка команды

Подстановка команды позволяет расширить возможности оболочки. Для подстановки используются конструкции `COMMAND` (обратные одиночные кавычки) или $(COMMAND)

Примеры

# greeting=`./hello`;                - запись содержимого текстового файла в переменную
# file_contents=$(cat $file);        - то же самое
# file_contents=$(<$file);
# variable1=`for i in 1 2 3; do
  echo -n "$i"
  done`

Ссылки:

Перенаправление ввода-вывода и каналы

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

Устройства ввода-вывода в linux соединены со специальными файлами, обозначаемыми дескрипторами:

  • дескриптор 0 - стандартный ввод (по умолчанию, клавиатура)
  • дескриптор 1 - стандартный вывод (по умолчанию, монитор)
  • дескриптор 2 - вывод стандартных ошибок (по умолчанию, монитор)

Виды каналов

  • выводной канал - символы >, >>. Примеры
  # command > временный.файл       - осуществить вывод первой команды во временный файл
  # временный.файл > command       - осуществить ввод второй команды из временного файла
  # command > /dev/null            - перенаправить вывод команды в "пустое" устройство а не на экран (работать "молча")
  # command > /dev/null 2>&1       - перенаправить вывод ошибок (дескриптор 2) на стандартный вывод (дескриптор 1), но только для текущей команды
  # command >> data-file           - перенаправить вывода сценария stdout в файл, в режиме добавления
  # exec 1>> data-file             - перенаправить стандартный вывод (например в скрипте) в файл постоянно
  • вводной канал - символы <, <<. Примеры
  # exec < data-file               - осуществить выполнение команды с аргументами из содержимого (вывода) файла
  символы << позволяют осуществить перенаправление на встроенный документ (скрипт) и
  передать интерактивной команде или программе, многострочный список команд, например
  # cat << End-of-message
    команды
  End-of-message
  где символ-ограничитель End-of-message может быть любым,
  но не должен повторяться в теле самого встроенного документа (команд)
  обычно это аббревиатура EOF
  например записать многострочный список в файл
  # cat << EOF > /path/file
    строки
  EOF
  • транзитный канал - символы |, < и >. Примеры
  # dmesg | less                          - соединить вывод первой команды с вводом второй
  # cpio -o <trashfiles.txt >trash.cpio   - пример создания транзитного канала через (именованный) файл

Рассмотренные ранее, создаваемые bash автоматически, каналы являются анонимными. Но существует возможность осуществлять соединение команд и через именованные каналы.
Для создания именованного канала с использованием утилиты mkfifo

  # mkfifo filename                - создание канала
  # grep fs < filename   (ENTER)   - ожидание ввода из канала, вводится в ПЕРВОЙ копии терминала
  # ls / > filename                - наполнение канала содержимым , вводится во ВТОРОЙ копии терминала
    таким образом образом получен эквивалент команды
  #  ls / | grep fs 
  но через именованный канал

Перенаправление ввода/вывода с помощью встроенной команды оболочки exec позволяет например

  • осуществить замену всего запланированного ввода из консоли (из stdin) на приём данных из файла, реализуя таким образом автоматическое выполнение интерактивного скрипта (макрос)
  • принять весь консольный вывод скрипта в файл(-лога), задавая его прямо в самом скрипте

Для перенаправления ввода со stdin на файл с помощью команды exec <filename

  #!/bin/bash        # пример скрипта подключения файла-ответов (макроса)
  exec 7<&0          # "запоминание" stdin в дескрипторе #7
  exec <macros.txt   # stdin заменяется файлом macros.txt
  read var           # получение первой строка из macros.txt в переменную var
  echo "Строка $var прочитана из файла macros.txt"
  exec 0<&6 6<&-     # восстановление stdin из дескрипторе #7 и его закрытие

Для перенаправления вывода со stdout в файл с помощью команды exec >filename

  #!/bin/bash        # пример скрипта с сохранением его вывода в файл
  exec 7>&1          # "запоминание" stdout в дескрипторе #7
  exec >logfile.txt  # stdout замещается файлом "logfile.txt".
  echo "Строка для stdout записывается в лог-файл logfile.txt"
  exec 1>&7 7>&-     # восстановление stdout из дескрипторе #7 и его закрытие

Ссылки:

Строковые преобразования

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

Преобразование строк встроенными средствами bash

  Синтаксис команды
  ${string/substring/replacement}             - замена первого вхождения подстроки substring в строке string на строку replacement
  ${string//substring/replacement}            - замена всех вхождений подстроки substring в строке string на строку replacement
  ${string/#substring/replacement}            - замена начала строки string соответствующего подстроке substring на строку replacement
  ${string/%substring/replacement}            - замена конца строки string соответствующего подстроке substring на строку replacement
  ${string:pos:length}                        - выделение подстроки из строки string длиной length наичнающейся с позиции pos
                                                первый символ в строке в Bash имеет номер 0.
  Примеры
  # stringZ=abcABC123ABCabc
  # echo ${stringZ/abc/xyz}                   - замена первого вхождения подстроки abc
    xyzABC123ABCabc
  # echo ${stringZ//abc/xyz}                  - замена всех вхождений подстроки abc
    xyzABC123ABCxyz
  # echo ${stringZ/abc}                       - удаление первого вхождения подстроки abc
    ABC123ABCabc
  # echo ${stringZ//abc}                      - удаление всех вхождений подстроки abc
    ABC123ABC
  # echo ${stringZ/#abc/XYZ}                  - удаление подстроки abc из начала строки
    XYZABC123ABCabc
  # echo ${stringZ/%abc/XYZ}                  - удаление подстроки abc с конца строки
    abcABC123ABCXYZ
  # echo ${stringZ:3:3}                       - выделение подстроки состоящей из символов 3-4-5 строки
    ABC

Преобразование с помощью awk

  # String=23skidoo1
  # echo | awk '{ print substr("'"${String}"'",3,4) }'     - выделение подстроки из строки аналогично ${string:pos:length}
                                                             первый символ в строке в Awk имеет номер 1.
    skid
  # echo | awk '{ print index("'"${String}"'", "skid") }'  - получение позиции начала подстроки в строке
    3

Ссылки:

Параллельные вычисления

Параллельные вычисления это такой способ построения программы (скрипта), когда программный код выполняется одновременно. В многопроцессорных системах Параллельные вычисления ускоряют выполнение программы. Bash позволяет организовать Параллельные вычисления с помощью запуска задач в фоне, использования каналов и утилиты xargs.

Для запуска Параллельных вычислений в фоне

  • с помощью специального символа & (амперсанда), например в скрипте
  #!/bin/bash
  sleep 60 && command &                 # запуск команды в фоне через 60 сек ожидания
  wait                                  # ожидание завершения ВСЕХ (!) порождённых (фоновых) процессов
  • с помощью консольного оконного менеджера screen, например в командной строке
  # screen -dm <программа>              # запуск <программы> в "отсоединенном" режиме

Для запуска Параллельных вычислений с помощью каналов, например в консоли

 # program 1 | program 2 | ...                 - запуск нескольких программ одновременно с возможностью их взаимодействия через каналы
 # program1 >/dev/null | program2 >/dev/null   - запуск двух программ одновременно без взаимодействия между собой

Для запуска Параллельных вычислений с помощью утилиты xargs

  # cat urllist | xargs -t -P 10 -n1 wget   # запуск нескольких (в пример 10) потоков скачивания

Ссылки:

Узелки на память

Именно чайники развивают самую кипучую деятельность. (Юмор программистов).

Узнать имя, путь и расширение файла

# full="ИМЯ_ФАЙЛА_С_ПУТЁМ"
# path="${full%/*}"
# filename=${full##*/}
# ext=${full##*.} 

Ссылки:

Существует ли функция

   if type NAME_FUNCTION >/dev/null 2>&1; then
      echo exist
   else
      echo no
   fi

Ссылки:

Экранирование пробелов

# FILE_NAME="$(ls <ИМЯ_ФАЙЛА_С_ПРОБЕЛАМИ>); \         - допустим в переменной $FILE_NAME слово с пробелами
  dirname "$FILE_NAME" | sed "s/ /\\\ /g")            - заменяем в имени везде где есть пробел на "\ "

Ссылки:

Случайное

Получение случайных чисел

 # echo $(( RANDOM%100 ))                               - получить случайное целое число до 100 средствами bash
 # echo | awk ' { srand(); print rand() } '             - получить случайное число от 0 до 1 средствами awk
 # head -1 /dev/urandom | od -N 1 | awk '{ print $2 }'  - получить случайное число из системного генератора

Получение случайных последовательностей (паролей)

 # pwgen 10 1 -s                                        - получить один 10-ти символьный безопасный пароль средствами pwgen
 # file=$(tempfile -d /dev/shm/); echo $file | awk -F '/dev/shm/' '{print $2} && rm $file; 
                                                        - получить случайную строку средствами tempfile

Получение случайных строк из файла

 # cat file | shuf | head -N                            - получить N случайных строк из файла
 # cat file | sort -R | tail -$N | while read str; do echo $str; done
                                                        - получить N случайных строк из файла

Получение случайных файлов каталога

 # a=( * ); echo "${a[RANDOM%${^C[@]}]}"|g'             - получить случайный файл из текущего каталога
 # echo {1..N} | sed 's| |\n|g' | sed 's|.*|a=( * ); echo "${a[RANDOM%${#a[@]}]}"|g' | bash;
                                                        - получить N случайных имён файлов из текущего каталога
 # ls -1 | shuf | tail-1                                - получить имя случайного файла из текущего каталога
 # ls -1 | shuf | head -N                               - получить N случайных имён файлов из текущего каталога

Ссылки:

Реализация команды GOTO

Команда GOTO в bash не существует, но может быть реализована программно.

Для реализации команды GOTO в bash может быть использован пример

#!/bin/bash

function goto {
    cmd=$(sed -n "/$1:/{:a;n;p;ba};" $0 | grep -v ':$')
    eval "$cmd"
    exit
}

start=${1:-"start"}
goto $start

start:
   echo start
   goto end
foo:
   echo foo
   goto start
end:

Ссылки:

Литература

Справочная информация

  • 02.04.2010: Программирование на bash: создание статьи в википедии
  • 16.10.2010: Программирование на bash: изменение внешнего вида, создание раздела о собственных скриптах
  • 27.12.2010: Программирование на bash: изменение внешнего вида, добавлен пример "Узнать расширение файла"
  • 13.03.2012: Программирование на bash: добавлено описание специальных переменных
  • 06.04.2012: Программирование на bash: добавлено описание перехвата прерываний
  • 14.07.2012: Программирование на bash: добавлено описание разбора параметров скрипта
  • 12.09.2012: Программирование на bash: добавлено описание форматирования вывода
  • 17.03.2016: Программирование на bash: добавлено описание реализации команды GOTO
  • 04.06.2016: Программирование на bash: добавлено описание реализации параллельных вычислений
  • 28.10.2016: Программирование на bash: добавлен раздел Строковые преобразования
  • 05.07.2018: Программирование на bash: добавлен раздел Скобки
  • 06.07.2018: Программирование на bash: добавлен раздел Подключение внешнего кода
  • 07.07.2018: Программирование на bash: добавлен раздел Случайное
Личные инструменты