Язык скриптов Bash в Linux

Введение в Bash

Bash (Bourne Again Shell) — командная оболочка и язык сценариев, используемый в UNIX-подобных системах.

Основное назначение: автоматизация задач, обработка файлов, системное администрирование.

Аналоги:

  • sh (Bourne shell) — устарела
  • ksh (KornShell) — устарела
  • csh (C shell), tcsh (TENEX C shell)
  • dash (Debian Almquist shell)
  • fish (friendly interactive shell)
  • zsh (Z shell), Oh My Zsh

Структура скриптов и их запуск

Скрипт — это обычный текстовый файл с командами Bash.

Комментарии — однострочные и начинаются с символа #.

Отступы игнорируются.

Если строка слишком длинная то её можно разделить на несколько строк с помощью символа \:

tar -cvpzf /backup/archive_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found \
    --exclude=/sys \
    /home/user/data

эквивалентно:

tar -cvpzf /backup/archive_$(date +%Y%m%d).tar.gz --exclude=/proc --exclude=/lost+found --exclude=/sys /home/user/data

Несколько команд можно объединить в одну строку с помощью символа ; (пробелы вокруг необязательны):

echo "HELLO" ; echo "WORLD"

эквивалентно:

echo "HELLO"
echo "WORLD"

Запуск скрипта вручную:

bash script.sh

Запуск скрипта как приложения:

  • Добавьте первой строкой скрипта shebang:
    #!/bin/bash
    
  • Добавьте право на выполнение:
    chmod +x script.sh
    
  • При запуске скрипта укажите каталог (например текущий каталог .):
    ./script.sh
    
  • Каталог можно не указывать, если скрипт находится в каталоге входящем в переменную окружения $PATH:
    script.sh
    

Если в shebang указать другой интерпретатор, то можно использовать другой скриптовый язык (Perl, Python, Ruby, PHP и т.д.).


Переменные

Все переменные — строкового типа.

Объявление переменных (без пробелов вокруг =):

NAME="Ivan"

Подстановка значения переменной:

echo "Hello, $NAME"

Кавычки не обязательны, можно использовать апострофы в качестве кавычек. Следующие выражения эквивалентны:

  • msg="Hello World"; echo $msg
  • msg=Hello\ World; echo $msg
  • msg='Hello World'; echo $msg

Если кавычки не используются, то специальные символы (включая пробел) надо экранировать с помощью символа \.

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

Встроенные переменные:

  • $0 — имя скрипта
  • $1, $2, ... — аргументы командной строки / аргументы функции
  • $# — количество аргументов
  • $@ — все аргументы
  • $? — код завершения последней вызванной команды

Для конкатенации строк достаточно просто поставить значения переменных рядом:

a="Hello"
b="World"
res="$a $b"

Для вычисления целочисленных арифметических выражений (+, -, *, /, %, ++, --) используются конструкции вида:

  • res=$(( 6 * 7 ))
  • res=$(expr 6 \* 7) (символ * нужно экранировать \, т.к. имеет спец. значение)
  • let res="6 * 7"

Если используется (( )) или let то экранирование * не требуется, также не нужно ставить $ перед переменными.

Если используется expr, то нужно экранировать * и ставить $ перед переменными.

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

Для вычисления вещественных арифметических выражений используется утилита bc:

# scale - количество знаков после запятой
# a() - арктангенс
# используем факт, что tg(pi/4) = 1
pi=$(echo "scale=10; 4*a(1)" | bc -l)
echo "Pi = $pi"

Ввод и вывод данных

  • Ввод с клавиатуры:
    read VAR
    
  • Вывод на экран:
    echo "Text"
    
  • Перенаправление потоков:
    • > — запись в файл потока вывода (перезапись)
    • 2> — запись в файл потока ошибок (перезапись)
    • &> — запись в файл потока вывода и ошибок (перезапись)
    • >> — добавление в файл потока вывода
    • 2>> — добавление в файл потока ошибок
    • &>> — добавление в файл потока вывода и ошибок
    • < — чтение потока ввода из файла
    • | — конвейер (передача вывода одной команды на вход другой команде)

Условные операторы

if [ $a -gt $b ]; then
  echo "a > b"
elif [ $a -eq $b ]; then
  echo "a = b"
else
  echo "a < b"
fi

Секции else и elif необязательны.

Секция elif может быть использована несколько раз.

Пробелы вокруг квадратных скобок в логических условия — обязательны.

Отступы слева внутри секций — необязательны.

С помощью символа ; происходит объединение двух операторов в одну строку. То есть:

if [ $a -gt $b ]; then
  echo "a > b"
fi

эквивалентно:

if [ $a -gt $b ]
then
  echo "a > b"
fi

Логические условия:

  • Числа: -eq (равно), -ne (не равно), -gt (больше), -lt (меньше), -ge (больше или равно), -le (меньше или равно)
  • Строки: = (равно), != (не равно), -z (пустая строка), -n (непустая строка)
  • Файлы:
    • -f — проверка что это обычный файл
    • -d — проверка что это каталог
    • -e — проверка что файл существует
    • -r — проверка что файл можно прочитать
    • -w — проверка что в файл можно записывать
    • -x — проверка что файл можно выполнить / в каталог можно перейти
  • Логические операторы:
    • COND1 && COND2 — И
    • COND1 -a COND2 — И (не рекомендуется, устаревший синтаксис)
    • COND1 || COND2 — ИЛИ
    • COND1 -o COND2 — ИЛИ (не рекомендуется, устаревший синтаксис)
    • ! EXPRESSION — НЕ

Вокруг логических выражений можно ставить круглые скобки отделенные пробелом.

Для проверки логических условий также может использовать команда test:

if test -d /tmp; then
  cp /etc/motd /tmp/motd
fi

Циклы

while (работает пока условие выполняется):

while [ $i -lt 5 ]; do
  echo $i
  ((i++))
done

for (арифметический цикл):

for ((i=0; i<5; i++)); do
  echo "$i"
done

for (обход элементов коллекции):

for file in *.txt; do
  echo "$file"
done

until работает пока условие не выполнится:

until [ $x -eq 10 ]; do
  ((x++))
done

Функции

function greet() {
  echo "Hello, $1"
}
greet "World"

Функциям можно передавать произвольное количество аргументов. Аргументы доступны внутри функции по их номеру, а не по имени. Поэтому скобки при объявлении функции всегда пустые или вообще могут быть опущены. Также допускается пропускать ключевое слово function:

function greet() {
  echo "Hello, $1"
}

эквивалентно:

function greet {
  echo "Hello, $1"
}

эквивалентно:

greet {
  echo "Hello, $1"
}

Возврат значения:

return 0

Код завершения функции доступен через встроенную переменную $?.


Отладка

Запуск с режимом трассировки:

bash -x script.sh

Прерывание при ошибке:

set -e