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

ShellBeginner
Практиковаться сейчас

Введение

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

  • Зелёная карточка (0) означает: «Я всё сделал правильно».
  • Красная карточка (1–255) означает: «Что-то пошло не так».

В Linux каждая команда работает именно так. Это называется кодом завершения или статусом завершения.

  • 0: успех;
  • Not 0: ошибка.

Большинство начинающих не обращает внимания на эти коды, потому что обычно они незаметны, пока вы не запросите их явно. Опытные разработчики скриптов внимательно следят за ними: статус завершения — один из основных способов, с помощью которых оболочка сообщает о результате выполнения команд и скриптов.

Это фундаментальный принцип Unix: небольшие программы обмениваются данными по простым соглашениям. Текст передаётся через стандартный вывод, а информация об успехе или ошибке — через статус завершения. Именно поэтому коды завершения так важны для автоматизации.

В этой лабораторной работе вы научитесь читать эти секретные карточки с помощью переменной $? и останавливать скрипты до того, как они создадут проблемы.

Проверка состояния команды с помощью $?

Как увидеть эту невидимую «оценочную карточку»? Сразу после выполнения команды нужно обратиться к специальной переменной $?.

Сначала рассмотрим успешное выполнение.

Откройте терминал и выведите список файлов:

ls -l

Команда выполнилась успешно, верно? Теперь спросим: «Какова оценка?»

echo $?

Вы должны увидеть 0. В логике Linux 0 означает отсутствие ошибок. Ноль ошибок — идеальный успех.

Теперь намеренно вызовем ошибку. Попробуйте вывести список несуществующего файла:

ls ghosts.txt

Linux сообщит, что не может найти этот файл. Снова запросите оценку:

echo $?

Вы увидите 2. Это не 0, значит, команда завершилась с ошибкой.

Точное ненулевое значение зависит от команды и ситуации, но пока достаточно запомнить простое правило: 0 означает успех, а любое другое значение — ошибку. Позже, работая со скриптами и инструментами, вы увидите, что некоторые программы используют разные ненулевые коды для обозначения разных типов проблем.

Ключевое правило: $? хранит только самое последнее состояние. Эта переменная запоминает лишь самую последнюю выполненную команду. Если вы выполните ls с ошибкой, затем echo "hello", а после этого проверите $?, то увидите 0, потому что команда echo выполнилась успешно. Проверяйте $? сразу после нужной команды.

Создание скрипта с ошибкой

Здесь начинается опасная часть. По умолчанию скрипт игнорирует красные карточки и просто продолжает работу.

Представьте скрипт, который выполняет такие действия:

  1. Создаёт резервную копию.
  2. Удаляет исходные файлы.

Если шаг 1 завершится с ошибкой, например из-за заполненного диска для резервных копий, шаг 2 всё равно выполнится — и вы потеряете всё.

Смоделируем такой «слепой скрипт».

Создайте файл copy_script.sh:

nano copy_script.sh

Введите этот код. Он пытается скопировать несуществующий файл, а затем сообщает об успешном завершении.

#!/bin/bash

## Attempt to copy a ghost
cp ghost_file.txt destination.txt

## Print a happy message
echo "Script finished execution"

Сохраните файл и выйдите из редактора. Сделайте скрипт исполняемым:

chmod +x copy_script.sh

Запустите его:

./copy_script.sh

Вы увидите сообщение об ошибке от cp, а затем сообщение Script finished execution. Скрипт столкнулся с ошибкой, но всё равно продолжил работу.

Проверьте код завершения самого скрипта:

echo $?

Команда вернёт 0. Почему? Потому что последняя команда (echo) выполнилась успешно. Этот скрипт вводит в заблуждение: его основная задача завершилась ошибкой, но системе он сообщил об успехе.

Это важный урок: у скрипта оболочки тоже есть статус завершения. Если не предпринять специальных действий, обычно он становится статусом последней выполненной команды. Поэтому скрипт может случайно скрыть ошибку, которая произошла раньше.

Реализация обработки ошибок

Нужно научить скрипт останавливаться на красный свет.

Обычно используется такая структура: «Попробовать выполнить команду X. Если код завершения НЕ равен 0, прекратить работу».

Синтаксис [ $? -ne 0 ] использует команду test, записанную в квадратных скобках. Параметр -ne означает «не равно» для чисел. Таким образом, оболочка спрашивает: «Не равен ли статус завершения предыдущей команды нулю?»

Отредактируйте скрипт copy_script.sh:

nano copy_script.sh

Измените его содержимое на следующее:

#!/bin/bash

cp ghost_file.txt destination.txt

## Check if the score is NOT equal (-ne) to 0
if [ $? -ne 0 ]; then
  echo "Error: The copy operation failed!"
  exit 1
fi

echo "Script finished execution"

Разбор:

  • if [ $? -ne 0 ]: проверка контролирует карточку;
  • exit 1: красная кнопка. Она немедленно завершает скрипт и устанавливает общий код завершения 1 (ошибка).

Команда exit завершает текущий скрипт оболочки и возвращает указанное число тому, кто его запустил: например, вашему терминалу, другому скрипту или системе автоматизации. Иными словами, вы не просто останавливаете выполнение локально — вы сообщаете статус внешней системе.

Сохраните файл и выйдите из редактора.

Запустите скрипт снова:

./copy_script.sh

Скрипт выведет сообщение об ошибке и завершится. Он не выведет Script finished execution.

Проверьте код завершения:

echo $?

Это будет 1. Теперь скрипт честно сообщает о своей ошибке.

Итоги

Вы научились понимать секретный язык команд.

В этой лабораторной работе вы попрактиковались:

  1. Читать оценочные карточки: использовать $?, чтобы определить, завершилась ли команда успешно (0) или с ошибкой (1–255).
  2. Выявлять обман: видеть, как скрипты могут скрывать ошибки при неосторожной обработке статусов.
  3. Использовать судью: применять exit 1, чтобы остановить выполнение при возникновении ошибки.

Этот шаблон «Проверить и завершить» отличает ненадёжный скрипт от профессионального.