Зачем нужно
По умолчанию bash не падает при ошибках - продолжает выполнять скрипт дальше, как ни в чём не бывало:
#!/usr/bin/env bash
cp /no/such/file /tmp/dest
▸ошибка, но скрипт идёт дальше
rm -rf /tmp/dest/*
▸теперь это удалит ЧТО-ТО ДРУГОЕ
Без strict mode такой скрипт молча крашит данные. Strict mode = три флага которые обязаны стоять в начале любого нетривиального скрипта:
set -euo pipefail
Что делает каждый флаг
| Флаг | Long name | Эффект |
|---|---|---|
-e | errexit | Упасть сразу на первой команде с exit-code != 0 |
-u | nounset | Упасть если используешь не установленную переменную |
-o pipefail | pipefail | Pipe считается упавшим если любое звено упало |
Без pipefail команда cmd1 | cmd2 имеет exit-code от последней
команды в пайпе. То есть false | true вернёт 0 (успех) - потому что
bash смотрит только на true. С pipefail - увидит что false
упал и pipe тоже упадёт.
Идиома
Стандартная прелюдия каждого production-скрипта:
#!/usr/bin/env bash
set -euo pipefail
# дальше твой код
Иногда добавляют -x для отладки (трассировка каждой команды в stderr):
set -euxo pipefail # с трассировкой
Или включают временно:
set -x
some_buggy_command
set +x # выключить
Когда нужно отключить локально
Иногда -e мешает: команда специально может вернуть != 0, и это OK.
Тогда - || true или явная проверка:
# diff возвращает 1 если файлы различаются - для нас это не ошибка
diff a.txt b.txt > result.txt || true
# Альтернатива - вырубить -e на одну команду
if some_check; then
echo ok
else
echo "check failed but we're fine with that"
fi
С -u похожая ловушка: ${VAR:-default} даёт значение по умолчанию
если переменная не установлена, без падения:
PORT=${PORT:-8080} # 8080 если PORT не заданecho "${OPTIONAL:-}" # пустая строка вместо ошибкиАльтернативы и нюансы
set -E- ловушкиtrap ERRнаследуются в функциях (по умолчанию нет)set -o functrace- то же для DEBUG/RETURN trapsshopt -s inherit_errexit--eнаследуется в command-substitution$(...)(тоже не работает по умолчанию!)
Полная защитная прелюдия которую видно в проде:
#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit # bash 4.4+
trap 'echo "ERROR at line $LINENO" >&2' ERR