Проверить команду в if
if сам смотрит на код команды, $? здесь не нужен. Запись
cmd; if [ $? -ne 0 ] работает, но ломается, стоит вставить между ними echo.
Код завершения — число от 0 до 255, которым команда сообщает итог: 0 — успех, другое число — сбой или ответ «нет».
ls /nope; echo $?
2 — код последней команды
grep -q root /etc/passwd && echo есть
вторая — после кода 0
mkdir /srv/app || echo "нет" >&2
вторая — после кода не 0
if grep -q '^backup:' /etc/passwd; then echo есть; fi
rc=$?
сохранить код, пока не затёрт
exit 1
выйти из скрипта с кодом 1
false | true; echo "${PIPESTATUS[@]}"
1 0 — коды каждого звена
set -o pipefail
упало звено — упал конвейер
Каждая команда, закончив работу, отдаёт оболочке
код завершения — целое число от 0 до 255. Ноль значит
«успех», любое другое число — сбой или ответ «нет». На экран код не выводится:
его хранит переменная $? до следующей команды.
Текст ошибки пишется для человека в поток ошибок. Программы
читают только код: if, &&, ||, while, systemd, cron и мониторинг.
| Код | Что значит |
|---|---|
| 0 | успех |
| 1 | общий сбой; у grep — «не нашёл», у diff — «файлы различаются» |
| 2 | ошибка вызова: ls не нашёл путь, grep не открыл файл |
| 124 | timeout прервал команду по времени |
| 126 | файл найден, но не запускается: нет права x |
| 127 | команда не найдена |
| 128+N | процесс убит сигналом N: 130 — Ctrl+C, 137 — kill -9, 143 — kill |
Код 128+N значит, что процесс завершил сигнал с номером N.
Смысл 1, 2 и других малых чисел у каждой программы свой — он описан в разделе
EXIT STATUS её man. У curl, например, 6 — «имя не найдено», 7 — «не удалось
подключиться», 28 — «вышло время», 22 — ответ HTTP 400 и выше при ключе -f.
a && b запускает b, только если a вернула 0. a || b запускает b,
только если a вернула не 0. Код всей строки — код последней выполненной
команды. ! перед командой меняет успех на сбой и обратно: ! true даёт 1.
Скрипт завершается с кодом последней выполненной команды, если раньше не
встретил exit N. exit без числа отдаёт код предыдущей команды. Код берётся
по модулю 256: exit 256 даёт 0, exit 300 — 44.
Код конвейера — код последнего звена: false | true вернёт 0.
Коды всех звеньев лежат в массиве PIPESTATUS. После set -o pipefail конвейер
возвращает код самого правого упавшего звена, а 0 — только если успешны все.
if сам смотрит на код команды, $? здесь не нужен. Запись
cmd; if [ $? -ne 0 ] работает, но ломается, стоит вставить между ними echo.
$? перезаписывает любая команда — echo, [ ], присваивание с подстановкой.
Нужен код дважды — сохрани его в переменную сразу.
Сообщение — в поток ошибок (>&2), код — не ноль: так скрипт понятен и человеку,
и тому, кто его вызвал.
Код curl отвечает на вопрос, состоялся ли обмен с сервером. Ответ 503 — тоже
состоявшийся обмен, поэтому код 0. Ключ -f делает сбоем ответы от 400 и выше
(код 22), а 204 и 302 для него — успех. Так отвечал учебный сервер:
| Ответ сервера | curl -s |
curl -sf |
curl -s -w '%{http_code}' |
|---|---|---|---|
| 200 | 0 | 0 | 200 |
| 204, 302 | 0 | 0 | 204, 302 |
| 404, 503 | 0 | 22 | 404, 503 |
| порт закрыт | 7 | 7 | 000 |
Нужен точный код ответа — печатай его ключом -w '%{http_code}' и сравнивай
в проверке. С -o и без -s curl рисует индикатор загрузки
в поток ошибок.
Принято: 1 — проверка не прошла, 2 — скрипт вызвали неправильно. Числа больше 125 не бери: 126, 127 и 128+N оболочка уже занимает своим смыслом.
Проверка смотрит на код не той команды:
echo OK выполнился после curl и записал в $? свой ноль. Проверяй саму
команду: if curl -sf …; then echo OK; fi.
Код скрипта — код последней команды. Так выглядит журнал службы
(journalctl -u clean.service -o cat), чей скрипт упал на второй строке, а
последняя команда (find) прошла:
Ставь || exit 1 после важных команд или set -e в начале скрипта (см.
bash-scripting).
Та же причина с другой стороны. Последняя строка вида
[ "$DEBUG" = yes ] && echo "готово" при пустой DEBUG вернула 1 — и скрипт
вместе с ней, хотя работа выполнена. Служба после такого скрипта — failed.
Заверши скрипт явным exit 0 или перепиши строку через if.
command not found: команды нет в PATH. Руками скрипт работает, а из cron или
таймера падает — там свой короткий PATH (см.
окружение таймера). Пиши полный путь к программе.
Файл найден, но не запускается: bash: ./check.sh: Permission denied. Нет права
x (chmod +x check.sh) или файловая система смонтирована с noexec.
Команду прервал сигнал: 130 — Ctrl+C, 143 — kill или systemctl stop, 137 —
kill -9, timeout -s KILL или нехватка памяти. Ищи, кто его послал, в
журнале: sudo journalctl -k | grep -i oom.
grep без совпадений возвращает 1. Для set -e и pipefail это сбой, и скрипт
выходит. Если «не нашёл» — нормальный исход, пиши grep … || true или проверяй
его в if.
c выполнится и тогда, когда упала b:
Где важно «либо одно, либо другое» (удалить или сообщить) — пиши if a; then b;
else c; fi.
exit $errors при 256 ошибках вернёт 0 — успех. Код не счётчик: при любой
ошибке выходи с 1, а число пиши в вывод.
set -euo pipefail, условия и циклы в скриптах.[ ] и [[ ]], которые тоже отвечают кодом.>&2 и конвейеры.systemctl status.$? после лишней команды и cd без || exit.Есть неточность? Сообщить об ошибке в статье