# Bash strict mode: set -euo pipefail _Процессы и ресурсы · LinuxLab Knowledge Base_ **TL;DR:** Три флага в начале bash-скрипта которые превращают его из «прощающего всё» в «упасть на первой же ошибке». Без них баги молча копятся. ## Зачем нужно По умолчанию bash **не падает** при ошибках - продолжает выполнять скрипт дальше, как ни в чём не бывало: ```bash #!/usr/bin/env bash cp /no/such/file /tmp/dest # → ошибка, но скрипт идёт дальше rm -rf /tmp/dest/* # → теперь это удалит ЧТО-ТО ДРУГОЕ ``` Без strict mode такой скрипт молча крашит данные. Strict mode = три флага которые **обязаны** стоять в начале любого нетривиального скрипта: ```bash 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-скрипта: ```bash #!/usr/bin/env bash set -euo pipefail # дальше твой код ``` Иногда добавляют `-x` для отладки (трассировка каждой команды в stderr): ```bash set -euxo pipefail # с трассировкой ``` Или включают временно: ```bash set -x some_buggy_command set +x # выключить ``` ## Когда нужно отключить локально Иногда `-e` мешает: команда специально может вернуть != 0, и это OK. Тогда - `|| true` или явная проверка: ```bash # 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}` даёт значение по умолчанию если переменная не установлена, без падения: ```bash PORT=${PORT:-8080} # 8080 если PORT не задан echo "${OPTIONAL:-}" # пустая строка вместо ошибки ``` ## Альтернативы и нюансы - `set -E` - ловушки `trap ERR` наследуются в функциях (по умолчанию нет) - `set -o functrace` - то же для DEBUG/RETURN traps - `shopt -s inherit_errexit` - `-e` наследуется в command-substitution `$(...)` (тоже не работает по умолчанию!) Полная защитная прелюдия которую видно в проде: ```bash #!/usr/bin/env bash set -Eeuo pipefail shopt -s inherit_errexit # bash 4.4+ trap 'echo "ERROR at line $LINENO" >&2' ERR ``` ## Команды ```bash set -euo pipefail ``` Стандартная защитная прелюдия - обязательна в начале любого скрипта ```bash set -x ``` Включить трассировку: bash печатает каждую команду перед выполнением ```bash PORT=${PORT:-8080} ``` Дефолтное значение переменной - обходит -u без отключения ```bash cmd || true ``` Игнорировать exit-code конкретной команды, не отключая -e глобально ```bash trap 'echo line $LINENO' ERR ``` Логировать строку где упал скрипт - must-have для длинных скриптов ## См. также - [Сигналы (SIGTERM, SIGKILL, SIGHUP)](/kb/signals.md) - [Процесс и PID](/kb/process-and-pid.md) - [File descriptors (stdin, stdout, stderr)](/kb/file-descriptors.md)