Строгий режим с set -euo pipefail
Включайте немедленное завершение при сбое и точно разбирайтесь, какие ошибки обнаруживает каждый флаг строгого режима, а какие пропускает
«Строгий режим с set -euo pipefail» — бесплатный урок Linux Command Line & Bash Scripting Mastery на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Linux Command Line & Bash Scripting Mastery, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.
Почему Bash по умолчанию молча игнорирует ошибки
По умолчанию Bash продолжает работу, даже если команды завершаются с ошибкой. Это приводит к малозаметным и трудным для отладки сбоям в рабочих сценариях.
Рассмотрим сценарий, который пытается создать резервную копию:
- Опечатка в пути приводит к сбою
cp - Bash игнорирует сбой и продолжает работу
- Сценарий сообщает об успехе, хотя данные так и не были сохранены в резервной копии
Это и есть проблема молчаливых сбоев. Строгий режим решает её, заставляя Bash вести себя подобно компилируемому языку: немедленно останавливать выполнение при возникновении проблемы.
#!/usr/bin/env bash
# Without strict mode — dangerous default behavior
cp /important/data /backups/data # fails (path doesn't exist)
echo "Backup complete" # still prints — false confidence!
rm -rf /tmp/staging # still runs — potentially destructiveТри основных флага: set -euo pipefail
Строгий режим включается добавлением этой строки в начале каждого сценария:
set -euo pipefail
Она активирует три независимых механизма защиты:
-e— немедленно завершать работу, если любая команда возвращает ненулевой статус-u— считать неустановленные переменные ошибкой (вместо подстановки пустой строки)-o pipefail— считать конвейер завершившимся с ошибкой, если сбой произошёл в любой его команде, а не только в последней
Вместе они образуют стандартный защитный заголовок серьёзных сценариев Bash. Каждый флаг выявляет свой тип ошибки.
#!/usr/bin/env bash
set -euo pipefail
echo "Strict mode is now active"
echo "Every command failure will abort the script"Разбор set -e (errexit)
set -e (также записывается как set -o errexit) немедленно завершает сценарий, когда команда завершается с ненулевым статусом.
Основные особенности:
- Простые команды:
false,grep pattern file(совпадений нет),ls /nonexistent— все они вызывают завершение - Код завершения последней команды сценария становится кодом завершения самого сценария
- Команды в условиях
ifне учитываются —-eне срабатывает для проверочного выражения - Команды, после которых стоит
|| true, также не учитываются (см. следующие разделы)
Воспринимайте -e как первую линию защиты от незаметного продолжения работы после сбоя.
#!/usr/bin/env bash
set -e
echo "Before failure"
ls /this/path/does/not/exist # exits here with code 2
echo "This line never runs"Разбор set -u (nounset)
set -u (также записывается как set -o nounset) заставляет Bash считать любое обращение к неустановленной переменной критической ошибкой.
Без -u опечатка вроде $FLENAME вместо $FILENAME молча заменяется пустой строкой, из-за чего команды начинают работать неожиданно — или опасно (представьте rm -rf "$TMPDIR/", если $TMPDIR не установлена).
Важные исключения:
${VAR:-default}— безопасная подстановка значения по умолчанию, которая не вызывает-u${VAR:+value}— условная подстановка, также безопасная"$@"и"$*"не учитываются, если позиционные аргументы не переданы
#!/usr/bin/env bash
set -euo pipefail
# Safe: provide a default for optional vars
OUTPUT_DIR="${1:-/tmp/output}"
LOG_LEVEL="${LOG_LEVEL:-info}"
echo "Writing to: $OUTPUT_DIR"
echo "Log level: $LOG_LEVEL"
# This would abort the script:
# echo "$UNDEFINED_VAR" # bash: UNDEFINED_VAR: unbound variableРазбор -o pipefail
Без pipefail статус завершения конвейера определяется исключительно последней командой. Сбои предыдущих команд молча игнорируются.
Пример без pipefail:
cat /missing/file | wc -lcatзавершается с кодом 1, ноwc -lуспешно завершается с кодом 0- Конвейер возвращает 0 — успех! Хотя данные были потеряны.
Если pipefail включён, Bash возвращает код завершения самой правой команды, завершившейся с ошибкой. Благодаря этому сбои в конвейере становятся видимыми и обрабатываемыми.
Примечание: pipefail не является буквенным флагом — его необходимо задавать с помощью -o pipefail.
#!/usr/bin/env bash
set -euo pipefail
# With pipefail: this aborts if grep finds nothing (exit 1)
# grep returns 1 when no match found
ps aux | grep "[n]ginx" | awk '{print $2}'
echo "If we reach here, nginx is running"Преднамеренные сбои команд — использование || true
Иногда команде разрешено завершиться с ошибкой. При использовании set -e необходимо явно указать, что сбой допустим, иначе сценарий прервётся.
Идиоматическое решение — || true, добавляющее запасной вариант, который всегда завершается успешно:
command || true— полностью игнорировать сбойcommand || echo "Warning: step failed, continuing"— записать предупреждение и продолжить работуcommand || { echo "fatal"; exit 1; }— использовать собственную обработку сбоя
Этот шаблон явно выражает намерение в коде: обычная команда означает «эта команда обязана завершиться успешно», а || true — «эта команда может завершиться с ошибкой, и это допустимо».
#!/usr/bin/env bash
set -euo pipefail
# Remove temp dir if it exists — OK if it doesn't
rm -rf /tmp/my_workspace || true
mkdir -p /tmp/my_workspace
# Check if a service is running — OK if not
if systemctl is-active --quiet nginx 2>/dev/null || true; then
echo "nginx is active"
fi
# Grep that may find nothing — OK
grep 'ERROR' /var/log/app.log || true
echo "Done"Что set -e НЕ перехватывает
У set -e есть хорошо известные исключения и особенности. Понимание их помогает избежать ложной уверенности:
- Команды в условиях
if/while/until— проверочное выражение намеренно не учитывается - Команды, перед которыми стоит отрицание
!—! falseне вызывает завершение - Последняя команда перед
||— например,false || handle_error - Статус завершения подоболочки в определённых контекстах — например,
VAR=$(failing_command)в некоторых версиях Bash - Возвращаемые значения функций — учитывается только последняя команда функции
Строгий режим не заменяет явную проверку ошибок — это страховочная сетка, которая выявляет большинство случайных сбоев.
#!/usr/bin/env bash
set -euo pipefail
# These do NOT trigger -e:
if false; then echo "never"; fi # -e exempt in conditions
! false # negation exempts
false || echo "handled" # || exempts the left side
# This DOES trigger -e (no condition, no ||):
# false
echo "Script continues after exempted failures"Подоболочки и функции в строгом режиме
Настройки строгого режима наследуются подоболочками, но в функциях и подстановках команд есть свои особенности.
Основные правила:
- Функции наследуют
-e,-uиpipefailот вызывающей оболочки - Ненулевое возвращаемое значение функции приводит к завершению вызывающей оболочки (если установлен
-e) — за исключением случаев, когда вызов находится в условии или после|| - Подстановка команды
$(): в старых версиях Bash сбой команды внутри$()может не вызвать-eв родительской оболочке; для безопасности сначала присвойте результат, а затем используйте его отдельной командой - Явные подоболочки
()наследуют все флаги
#!/usr/bin/env bash
set -euo pipefail
setup_workspace() {
local dir="$1"
mkdir -p "$dir" # fails here if permissions denied
cd "$dir"
echo "Ready in $(pwd)"
}
# Safe pattern: assign result then use it
TODAY=$(date +%Y-%m-%d) # capture separately
WORKDIR="/tmp/run_${TODAY}"
setup_workspace "$WORKDIR"
echo "Workspace: $WORKDIR"Сочетание строгого режима с перехватом ошибок
Строгий режим указывает Bash, когда остановиться. Обработчик trap для ERR позволяет выполнить очистку или диагностику до завершения сценария.
Распространённый шаблон:
- Включить строгий режим в начале сценария
- Определить функцию
cleanupилиon_error - Зарегистрировать её с помощью
trap 'on_error' ERR - При необходимости также установить обработчик для
EXIT, чтобы очистка гарантированно выполнялась независимо от успеха или сбоя
Важно: используйте set -E (заглавная E, также называется errtrace), чтобы обработчик ERR наследовался функциями и подоболочками. Без этого обработчики срабатывают только в основном теле оболочки.
#!/usr/bin/env bash
set -Eeuo pipefail
on_error() {
local exit_code=$?
local line_number=${BASH_LINENO[0]}
echo "ERROR: command failed with code ${exit_code} at line ${line_number}" >&2
}
cleanup() {
echo "Cleaning up temporary files..." >&2
rm -rf /tmp/my_run_dir 2>/dev/null || true
}
trap on_error ERR
trap cleanup EXIT
mkdir -p /tmp/my_run_dir
echo "hello" > /tmp/my_run_dir/output.txt
cat /tmp/my_run_dir/output.txt
echo "Done"Локальное отключение строгого режима
Иногда блок кода намеренно бывает «нестрогим» — например, при проверке наличия необязательных инструментов или запуске устаревших команд, которые возвращают ненулевой статус не из-за ошибки. Строгий режим можно временно отключить, а затем восстановить.
Безопасный шаблон:
- Сохраните состояние с помощью
set +e(отключает-e), выполните блок, а затем снова включите режим с помощьюset -e - Или используйте подоболочку
( set +e; ... ), чтобы флаги родительской оболочки не изменялись - Всегда включайте флаги снова сразу после завершения рискованного блока — оставленные отключёнными флаги часто становятся источником ошибок
Если блок содержит несколько команд, предпочитайте вариант с подоболочкой: при завершении она автоматически восстанавливает флаги.
#!/usr/bin/env bash
set -euo pipefail
# Probe for optional tools without aborting
HAS_JQ=false
(
set +e
command -v jq > /dev/null 2>&1
[[ $? -eq 0 ]] && echo "jq_found"
) && HAS_JQ=true || true
if [[ "$HAS_JQ" == "true" ]]; then
echo "jq is available — using JSON output"
else
echo "jq not found — using plain text"
fiПолный шаблон сценария со строгим режимом
Ниже приведён готовый для рабочей среды шаблон, объединяющий все рассмотренные в этом уроке рекомендации по строгому режиму:
set -Eeuo pipefail— все четыре флага, включаяerrtraceIFS=$'\n\t'— более безопасное разделение слов (не разделяет по пробелам)- Обработчики ERR и EXIT для диагностики и очистки
- Явные значения по умолчанию для необязательных параметров
readonlyиlocalдля ограничения области видимости переменных
Копируйте этот шаблон в начало каждого нетривиального сценария Bash, чтобы сразу воспользоваться остановкой при первом сбое и возможностью отследить ошибки.
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
# ── Constants ────────────────────────────────────────────
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# ── Trap handlers ────────────────────────────────────────
err_handler() {
echo "[${SCRIPT_NAME}] ERROR on line ${BASH_LINENO[0]}: exit ${?}" >&2
}
cleanup() {
echo "[${SCRIPT_NAME}] Exiting" >&2
}
trap err_handler ERR
trap cleanup EXIT
# ── Defaults ─────────────────────────────────────────────
ENV="${1:-production}"
MAX_RETRIES="${MAX_RETRIES:-3}"
# ── Main ─────────────────────────────────────────────────
main() {
echo "Running in env=${ENV}, max_retries=${MAX_RETRIES}"
echo "Script dir: ${SCRIPT_DIR}"
}
main "$@"Проверка знаний: поведение pipefail
Проверьте, как pipefail влияет на коды завершения конвейера.
Итоги: строгий режим с set -euo pipefail
В этом уроке Вы узнали, как с помощью строгого режима сделать так, чтобы сценарии Bash быстро завершались при сбоях и явно сообщали об ошибках.
Три флага и то, от чего они защищают:
-e(errexit) — завершает выполнение при любом ненулевом статусе команды; не действует в условиях и после||-u(nounset) — прерывает выполнение при обращении к неустановленной переменной; для необязательных переменных используйте${VAR:-default}-o pipefail— считает весь конвейер завершившимся с ошибкой, если сбой произошёл на любом этапе, а не только на последнем
Дополнительные рекомендации:
- Добавляйте
-E(errtrace), чтобы ловушки ERR распространялись на функции - Используйте
trapсERRиEXITдля диагностики и очистки - Используйте
|| true, чтобы намеренно допускать сбои - Временно отключайте строгую обработку с помощью
set +eво вложенных оболочках для устаревшего кода или проверочных операций
Строгий режим не является универсальным решением — важно знать его исключения, — но это самая эффективная привычка для написания надёжных и защищённых от сбоев сценариев Bash.
Часто задаваемые вопросы
Урок «Строгий режим с set -euo pipefail» бесплатный?
Да — полный текст урока «Строгий режим с set -euo pipefail» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Linux Command Line & Bash Scripting Mastery, подпишись на CoddyKit PRO. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.
Чему я научусь в уроке «Строгий режим с set -euo pipefail»?
Включайте немедленное завершение при сбое и точно разбирайтесь, какие ошибки обнаруживает каждый флаг строгого режима, а какие пропускает Ты практикуешь Linux Command Line & Bash Scripting Mastery с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Linux Command Line & Bash Scripting Mastery?
Предыдущий опыт не требуется. Linux Command Line & Bash Scripting Mastery на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Строгий режим с set -euo pipefail»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Linux Command Line & Bash Scripting Mastery?
Да. Каждый урок Linux Command Line & Bash Scripting Mastery включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Строгий режим с set -euo pipefail
- Обработчики trap для очистки и сигналов
- Безопасные временные файлы и каталоги блокировок
- Идемпотентные скрипты и логика повторных попыток с задержкой