Надёжность: set -euo pipefail, trap, отладка

Делаем скрипты предсказуемыми: останавливаемся на ошибках и убираем за собой.

set -euo pipefail — это набор строгих режимов Bash, которые заставляют скрипт падать на первой же ошибке вместо тихого продолжения.

Проблема: Bash слишком прощающий

По умолчанию Bash игнорирует ошибки: если команда упала, скрипт спокойно идёт дальше — и можно, например, удалить файлы в пустой переменной пути. Лечится строгим режимом в начале скрипта:

#!/usr/bin/env bash
set -euo pipefail

Разберём по буквам: -e — выйти при ошибке любой команды; -u — ошибка при использовании необъявленной переменной; -o pipefail — конвейер падает, если упала любая его команда, а не только последняя.

ФлагЧто делает
-eвыход при первой ошибке
-uошибка на необъявленной переменной
-o pipefailловит сбой внутри конвейера

Зачем -u спасает от катастроф

#!/usr/bin/env bash
set -u
rm -rf "$BUILDDIR/cache"

Если BUILDDIR забыли задать, без -u путь стал бы /cache и могла начаться беда. С -u скрипт сразу упадёт с ошибкой «unbound variable» — и ничего не удалит.

trap: уборка при выходе

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

#!/usr/bin/env bash
set -euo pipefail

tmpfile=$(mktemp)
cleanup() {
  rm -f "$tmpfile"
  echo "Временные файлы удалены"
}
trap cleanup EXIT

echo "данные" > "$tmpfile"
# ... работа со скриптом ...

Команда trap cleanup EXIT гарантирует, что функция cleanup выполнится при ЛЮБОМ завершении скрипта — нормальном, по ошибке или по Ctrl+C. Временный файл точно удалится.

Отладка через set -x

Когда скрипт ведёт себя загадочно, включите трассировку: set -x печатает каждую команду перед выполнением с подставленными переменными.

set -x
name="мир"
echo "Привет, $name"
set +x

Вывод:

+ name=мир
+ echo 'Привет, мир'
Привет, мир

Строки с + — это трассировка: видно реальные значения переменных. set +x выключает режим. Можно запустить весь скрипт с трассировкой: bash -x script.sh.

Как работает под капотом

Флаги set переключают внутренние режимы оболочки. -e заставляет Bash проверять код возврата каждой простой команды и завершаться при ненулевом (с рядом исключений — например, в условиях if). trap регистрирует обработчик на сигнал или псевдособытие EXIT; Bash вызывает его перед фактическим завершением. -x печатает каждую раскрытую команду в stderr с префиксом из переменной PS4 (по умолчанию +).

Частые ошибки

  • -e и команды, которые «нормально» возвращают не 0. Например, grep без совпадений вернёт 1 и уронит скрипт. Оборачивайте: grep x file || true.
  • Забыть pipefail. Без него ложная_команда | tee log считается успешной, потому что tee отработал.
  • trap без EXIT. Уборку вешайте на EXIT, чтобы она срабатывала при любом выходе, а не только по конкретному сигналу.

Итог

  • set -euo pipefail в начале скрипта превращает тихие ошибки в явные падения.
  • trap cleanup EXIT гарантирует уборку при любом завершении скрипта.
  • set -x (или bash -x) трассирует выполнение — главный инструмент отладки.
Проверьте себя
1. Что делает флаг -e в set -euo pipefail?
AВключает echo
BЗавершает скрипт при первой же ошибке команды
CЭкспортирует переменные
DВключает отладку
2. Зачем нужен trap cleanup EXIT?
AЧтобы поймать опечатки
BЧтобы функция cleanup выполнилась при любом завершении скрипта — нормальном или по ошибке
CЧтобы ускорить скрипт
DЧтобы экспортировать переменные
3. Зачем добавляют || true к команде вроде grep при включённом set -e?
AДля скорости
Bgrep без совпадений возвращает 1, что при set -e уронит скрипт; || true гасит это
CЧтобы grep искал быстрее
Dtrue обязателен после grep