Модуль 03 — Коды возврата и условия

Скрипт, который проверяет, прежде чем действовать

Прогресс курса Модуль 3 из 9

Что вы освоите в этом модуле

01

Код возврата

Каждая команда, завершаясь, возвращает число от 0 до 255 — код возврата (exit status). 0 — успех, всё остальное — ошибка; что значит конкретное число, описано в man команды. Код последней команды хранится в $?.

$ true; echo $?
0
$ false; echo $?
1
$ ls /nonexistent >/dev/null 2>&1; echo $?
2
$ grep -q root /etc/passwd; echo $?        # -q: ничего не выводить, только код
0
$ grep -q nobodyxyz /etc/passwd; echo $?
1
КодОбычное значение
0Успех. Для grep — найдено
1Общая ошибка. Для grep — не найдено (это не сбой)
2Неверное использование: ошибка в аргументах
126Файл найден, но не исполняемый
127Команда не найдена
128+NЗавершён сигналом N: 130 — Ctrl+C, 143 — TERM

$? перезаписывается после каждой команды, даже после echo. Если код нужен дальше — сохраните сразу: rc=$?.

02

&& и ||

а && б — выполнить б, только если а успешна. а || б — выполнить б, только если а завершилась ошибкой.

$ mkdir -p /srv/backup && echo "создан"
$ cd /nonexistent || echo "нет каталога"
-bash: cd: /nonexistent: Нет такого файла или каталога
нет каталога
$ sudo nginx -t && sudo systemctl reload nginx   # перезагрузка — только при верной конфигурации

Самый важный приём: не продолжать, если шаг не удался. cd в несуществующий каталог оставит вас там, где вы были, и следующая команда сработает не там:

cd /var/backup/old
rm -rf *                       # если cd не удался — удалится всё в текущем каталоге

cd /var/backup/old || exit 1  # правильно: при ошибке — выйти
rm -rf -- *
а && б || в — не «если-то-иначе». в выполнится и тогда, когда а успешна, но упала б. Для ветвления пишите if.
03

if

if выполняет команду и смотрит на её код возврата. Условие — это любая команда, а не только сравнение:

if grep -q "^$user:" /etc/passwd; then
    echo "пользователь $user есть"
elif [[ $user == root ]]; then
    echo "странно: root не найден"
else
    echo "пользователя $user нет"
fi

Пример целиком — скрипт, который определяет, что за путь ему дали:

#!/bin/bash
# check.sh ПУТЬ — что это за путь
path=$1
if [[ -z $path ]]; then
    echo "Использование: $0 ПУТЬ" >&2
    exit 2
fi
if [[ -d $path ]]; then
    echo "$path — каталог"
elif [[ -f $path ]]; then
    echo "$path — файл, $(stat -c %s "$path") байт"
else
    echo "$path — не существует" >&2
    exit 1
fi
$ ./check.sh /etc
/etc — каталог
$ ./check.sh /etc/hostname
/etc/hostname — файл, 7 байт
$ ./check.sh /nope; echo "код $?"
/nope — не существует
код 1
$ ./check.sh; echo "код $?"
Использование: ./check.sh ПУТЬ
код 2

>&2 отправляет сообщение в stderr: ошибки не должны смешиваться с результатом, который могут читать другие программы.

04

Проверки [[ ]]

[[ … ]] — команда bash для проверок: возвращает 0, если условие верно. Пробелы после [[ и перед ]] обязательны.

ПроверкаИстинно, если
-e путьПуть существует
-f путь / -d путьОбычный файл / каталог
-s файлФайл не пустой
-r, -w, -xМожно читать / писать / запускать
a -nt bФайл a новее b
-z $s / -n $sСтрока пустая / непустая
$a == $b, $a != $bСтроки равны / не равны; справа может быть шаблон: $f == *.pdf
$s =~ регвырСовпадает с регулярным выражением; группы — в BASH_REMATCH
$a -eq $b, -ne, -lt, -le, -gt, -geСравнение чисел
! усл, усл1 && усл2, усл1 || усл2Не, и, или
$ [[ report.pdf == *.pdf ]] && echo pdf
pdf
$ [[ "2026-10-14" =~ ^([0-9]{4})-([0-9]{2}) ]] && echo "год ${BASH_REMATCH[1]}, месяц ${BASH_REMATCH[2]}"
год 2026, месяц 10
$ [[ 10 > 9 ]] && echo да || echo нет
нет

Последний пример — ловушка: > и < внутри [[ ]] сравнивают строки по алфавиту, а «10» по алфавиту раньше «9». Числа сравнивают через -gt или в (( )).

05

[ ] и (( ))

[ … ] — старая команда test из POSIX: работает в любой оболочке, включая dash, но с подвохами. Внутри неё переменные разбиваются на слова, поэтому кавычки обязательны:

$ [ $undefined = x ]; echo $?
-bash: [: =: ожидается унарный оператор
2
$ [[ $undefined = x ]]; echo $?
1

Пустая переменная без кавычек исчезла, и [ получил бессмысленное = x. В bash-скриптах используйте [[ ]]; [ "$var" = x ] с кавычками — только для скриптов под #!/bin/sh.

(( … )) — арифметическое условие: истинно, если выражение не равно нулю. Для чисел читается естественнее всего:

$ x=5
$ if (( x > 3 && x < 10 )); then echo "в диапазоне"; fi
в диапазоне
$ used=$(df --output=pcent / | tail -1 | tr -dc 0-9)
$ (( used >= 80 )) && echo "диск заполнен на $used%" >&2
06

case

Когда одно значение сравнивается со многими вариантами, case короче цепочки elif. Варианты — шаблоны, как для имён файлов; | — «или», * — всё остальное. Каждая ветка заканчивается ;;.

#!/bin/bash
case $1 in
    start|run)  echo "запускаю" ;;
    stop)       echo "останавливаю" ;;
    *.conf)     echo "файл конфигурации $1" ;;
    *)          echo "Использование: $0 start|stop" >&2; exit 2 ;;
esac
$ ./svc.sh run
запускаю
$ ./svc.sh nginx.conf
файл конфигурации nginx.conf
$ ./svc.sh restart; echo $?
Использование: ./svc.sh start|stop
2
07

exit: код возврата скрипта

Скрипт тоже возвращает код: тот, что указан в exit N, или код последней выполненной команды. Его проверяют cron, systemd, конвейеры CI/CD и другие скрипты — поэтому скрипт должен честно сообщать о провале.

Соглашение для своих скриптов

  • exit 0 — задача выполнена.
  • exit 1 — задача не выполнена: не хватило места, нет доступа, внешняя команда упала.
  • exit 2 — неверный вызов: нет аргумента, неизвестная опция. Вместе с сообщением «Использование: …».
  • Сообщения об ошибках — в stderr (>&2), результат — в stdout.

Проверка в начале скрипта, что его запустили с нужными правами, — типичный пример:

if (( EUID != 0 )); then
    echo "Запустите через sudo" >&2
    exit 1
fi

Ключевые выводы модуля

Запомните главное

  • 0 — успех, не 0 — ошибка; код последней команды — $?
  • cd каталог || exit 1 — не продолжать после неудачного шага
  • if проверяет код любой команды; [[ ]] — для файлов и строк, (( )) — для чисел
  • > в [[ ]] сравнивает строки, числа — -gt или (( a > b ))
  • case — выбор по шаблонам; *) ловит всё остальное
  • Скрипт завершается осмысленным кодом, ошибки пишет в stderr
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Модуль 02: Переменные Все модули Модуль 04: Циклы