Коды возврата и обработка ошибок команд

Учимся понимать, успешно ли отработала команда, и реагировать на сбои.

Код возврата (exit code) — это число от 0 до 255, которое команда оставляет после себя: 0 означает успех, любое другое — конкретную ошибку.

Переменная $? хранит результат

Сразу после команды её код возврата лежит в специальной переменной $?.

ls /etc > /dev/null
echo "Код: $?"
ls /not-exist 2> /dev/null
echo "Код: $?"

Вывод:

Код: 0
Код: 2

Первая команда успешна (0), вторая не нашла путь и вернула 2. Каждая программа сама определяет, какие коды что значат, но 0 всегда успех.

exit завершает скрипт с кодом

Скрипт тоже возвращает код — через exit N. По нему вызывающая система (например, CI или cron) понимает, всё ли хорошо.

#!/usr/bin/env bash
if [ ! -f "$1" ]; then
  echo "Файл не найден" >&2
  exit 1
fi
echo "Файл на месте"
exit 0

Обратите внимание на >&2 — сообщение об ошибке мы отправляем в stderr, как и положено.

Цепочки && и ||

Операторы && и || выполняют следующую команду в зависимости от успеха предыдущей.

ЗаписьКогда выполнится вторая команда
A && Bесли A успешна (код 0)
A || Bесли A неуспешна (код не 0)
mkdir -p /tmp/build && echo "папка готова"
cd /no-such-dir || echo "не смог перейти"

Вывод:

папка готова
не смог перейти

Это компактный способ написать «сделай Б, только если А удалась» или «иначе предупреди».

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

Когда процесс завершается, операционная система сохраняет его код выхода, и Bash кладёт его в $?. Конвейер A | B по умолчанию возвращает код только последней команды (B) — об этом важно помнить при обработке ошибок. Операторы && и || используют «ленивое» вычисление: A && B не запустит B, если A уже провалилась, потому что результат и так известен.

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

  • Проверять $? слишком поздно. $? хранит код ПОСЛЕДНЕЙ команды; даже echo между проверкой и командой перезапишет его. Проверяйте сразу.
  • Возвращать exit 0 при ошибке. Тогда CI или cron решат, что всё хорошо. Возвращайте ненулевой код при сбое.
  • Путать && и ||. && — «при успехе», || — «при неудаче».

Итог

  • Код возврата: 0 — успех, иначе — ошибка; он лежит в $? сразу после команды.
  • exit N завершает скрипт с кодом, по которому судят об успехе извне.
  • A && B запускает B при успехе A, A || B — при неудаче A.
Проверьте себя
1. Какой код возврата означает успешное выполнение команды?
A1
B0
C200
DЛюбой положительный
2. Что выполнит конструкция A || B?
AB всегда
BB только если A завершилась успешно
CB только если A завершилась с ошибкой (код не 0)
DA и B параллельно
3. Почему $? нужно проверять сразу после нужной команды?
A$? медленно вычисляется
BЛюбая следующая команда, даже echo, перезапишет $? своим кодом
C$? работает только в функциях
D$? всегда равен 0