Войти в сентябрь было непросто, но большую часть задач удалось успешно закрыть. В очередной раз убеждаюсь, что не стоит давать разработчикам root доступ к серверам. Эта оплошность стоила мне 3х дней безвылазного дебага на проде и кучи нервов. Ладно, всякое бывает. Сегодня поговорим про статусы выхода в bash.

Каждая программа в linux возвращает некий статус, был ли успех по завершению или возникла ошибка. Этими статусами можно гибко управлять в своих bash скриптах для отображения нужных ошибок.

К примеру мой вело-скрипт для бэкапов основан на команде tar и в какой-то момент оно начало возвращать ошибку. Бэкапы соответственно перестали делаться, но я про это ничего не знаю. Но добавив в скрипт обработку ошибок, я получил неплохой триггер, который в случае факапа накричит мне в телеграм. А я успешно это прочитаю и забью болт рабочие будни.

Статус выхода, это целое число от 0-255, где 0 это успешное завершение команды/программы. Все остальные коды, это ошибки.

Например, если программа не найдена ни в одном из путей $PATH либо ее вообще нет в системе, то оболочка вернет 127 код ошибки. Ну а если команда/программа найдена, но не является исполняемой, то мы получим код 126 (Permission denied).

Чтобы сделать программу/скрипт исполняемым, ставь специальный атрибут: chmod +x <program/script>

Как отловить код выхода из программы/скрипта

Все очень просто, существует специальная зарезервированная переменная «$?», которая будет хранить в себе как раз тот самый код выхода. Теперь наглядно, запускаем:

# date
# echo $?
0

Видим что прилетел 0, окей, команда отработала успешно. Теперь запустим так:

# bashdays
# echo $?
127

Как видим вернулся код 127, как я писал выше, этот код означает, что не удалось найти программу с именем bashdays.

Понятно? Думаю вообще все прозрачно. Имея эту информацию, можно строить гибкую логику в скриптах и обрабатывать эксепшены.

Чтобы сохранить код в обычную переменную, делаем так:

#/bin/bash

date
code=$?
echo "exit code : ${code}"

Теперь в переменной code будет храниться код завершения. Даже если ты запустишь следом другие команды, в переменной code будет лежать предыдущий код выхода.

Рассмотрим ситуацию с логикой:

#!/bin/bash

date
code=$?

if test $code -eq 0
then
  echo "success"
else
  echo "failed"
fi

test - предназначена для проверки типа файла и сравнения чисел и строк. Возвращает код возврата 0 (истина) или 1 (ложь) в зависимости от вычисления выражения. Выражения могут быть как унарными, так и бинарными.

Если команда date вернет 0 (-eq - означает равно), скрипт завершится со статусом 0 = success, во всех других статусах мы получим failed.

Также ты можешь самостоятельно возвращать нужные тебе коды в своих скриптах. Я использую этот способ в пайплайнах, когда gitlab считает что завершил сборку успехом, но на самом деле это не так. Подкидываешь ему exit 1 и пайплайн уже не зеленый, а красный.

Рассмотрим пример:

#!/bin/bash

date
time
exit 1
echo "hello"

После запуска, выполнятся две команды date и time, затем всё завершится с кодом 1 не передав управление команде echo.

Применение exit очень удобно для дебага, работает как breakpoints, вставляешь exit где нужно завершить скрипт и вся оставшаяся логика не выполняется. Можно конечно ненужный кусок кода просто закомментировать, но согласись проще вписать exit не трогая ничего остального.

Тема вроде очень простая, но всегда вызывает вопросы и недоумение — В смысле код ошибки? Какой еще ошибки? А что так можно было?

Роман Шубин
Роман Шубин
CEO & CTO, Главред в «Цифровой улей»
Задать вопрос
Короче говоря имея на руках код завершения, можно проверять результаты выполнения команд и триггерить что-то нужное.

Давай краба, завтра увидимся, хорошо тебе догулять выходные!