Обработка ошибок с помощью trap

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

Введение

При выполнении скрипт часто создаёт временные файлы или запускает фоновые процессы. Если пользователь прерывает скрипт, например нажатием Ctrl+C, эти временные файлы нередко остаются в системе и засоряют её.

Чтобы этого не происходило, скрипту нужно уметь определять момент остановки и выполнять необходимые действия по очистке.

В Linux для перехвата сигналов прерывания и событий завершения используется команда trap. Сигналы — один из способов, с помощью которых операционная система взаимодействует с процессами. Например, нажатие Ctrl+C обычно отправляет процессу сигнал SIGINT, предлагая ему остановиться.

Это позволяет определить процедуру «при выходе», чтобы перед завершением скрипт успел удалить созданные им временные данные.

В этой лабораторной работе вы:

  1. Создадите скрипт, который генерирует временные файлы.
  2. Прервете скрипт, чтобы увидеть оставшиеся файлы.
  3. Настроите trap, чтобы временные файлы автоматически удалялись при завершении.

Создайте долгий процесс

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

Долгие процессы часто используются в задачах администрирования. Скрипт может отслеживать журнал, ожидать ввод, опрашивать сервис или последовательно обрабатывать задания. Чем дольше он работает, тем важнее очистка после прерывания.

Создайте и откройте файл long_process.sh в каталоге проекта:

cd ~/project
nano long_process.sh

Введите следующий код:

#!/bin/bash

## Create a temporary file
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."

## Fall asleep (Simulate a long job)
echo "Process is running. Press Ctrl+C to stop..."
while true; do
  sleep 1
done

Сохраните файл (Ctrl+O, Enter) и выйдите из редактора (Ctrl+X).

Сделайте файл исполняемым:

chmod +x long_process.sh

Проблема: если запустить этот скрипт, а затем остановить его с помощью Ctrl+C, файл temp_data.txt останется на диске. Это считается плохой практикой: при многократных запусках дисковое пространство может заполниться ненужными файлами.

Временные файлы — классический пример, но такая же проблема может возникнуть с файлами блокировок, сокетами, точками монтирования или вспомогательными фоновыми процессами. Очистка нужна для того, чтобы система после завершения работы оставалась в предсказуемом состоянии.

Определите функцию очистки

Чтобы правильно выполнять очистку, рекомендуется определить отдельную функцию, содержащую всю логику очистки.

Отдельная функция делает скрипт более структурированным. Кроме того, одну и ту же логику очистки можно повторно использовать при разных вариантах завершения, не копируя её в нескольких местах.

Снова откройте скрипт:

nano long_process.sh

Добавьте функцию cleanup в начало скрипта:

#!/bin/bash

## Define the cleanup function
cleanup() {
  echo ""
  echo "Cleaning up temporary files..."
  rm -f temp_data.txt
  echo "Cleanup complete."
}

## Create a temporary file
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."

## Simulate a long-running process
echo "Process is running. Press Ctrl+C to stop..."
while true; do
  sleep 1
done

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

На этом этапе функция определена, но скрипт ещё не знает, когда её вызывать.

Настройте trap для перехвата сигналов

Теперь с помощью команды trap сообщите Bash: «Если вы получите сигнал прерывания, например после нажатия Ctrl+C, выполните функцию cleanup».

Это один из наглядных примеров того, как скрипт оболочки реагирует на события операционной системы, а не только на текстовый ввод. Сигнал — это асинхронное сообщение от системы или пользователя процессу, а trap даёт скрипту возможность преднамеренно отреагировать на него.

Откройте скрипт:

nano long_process.sh

Добавьте команду trap после определения функции, но до начала основной логики.

#!/bin/bash

cleanup() {
  echo ""
  echo "Cleaning up temporary files..."
  rm -f temp_data.txt
  echo "Cleanup complete."
  exit
}

## Register the trap
## SIGINT = Ctrl+C
## EXIT = Any exit (success or failure)
trap cleanup SIGINT EXIT

## The rest of the script...
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."

echo "Process is running. Press Ctrl+C to stop..."
while true; do
  sleep 1
done

Разбор аргументов trap:

  • cleanup: команда или функция, которую нужно выполнить;
  • SIGINT: сигнал, отправляемый при нажатии Ctrl+C;
  • EXIT: псевдосигнал, срабатывающий при любом завершении скрипта.

Использование одновременно SIGINT и EXIT делает очистку надёжнее. SIGINT обрабатывает обычное прерывание с клавиатуры, а EXIT служит более общим механизмом защиты при нормальном завершении и многих ошибках.

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

Проверьте очистку

Теперь убедитесь, что механизм работает.

Запустите скрипт:

./long_process.sh

Скрипт создаст temp_data.txt и начнёт выполняться.

Прервите его, нажав Ctrl+C.

Вы должны увидеть такой вывод:

^C
Cleaning up temporary files...
Cleanup complete.

Наконец, проверьте, что файл удалён:

ls temp_data.txt

Команда должна вернуть сообщение об ошибке, похожее на это:

ls: cannot access 'temp_data.txt': No such file or directory

Команда trap успешно перехватила сигнал остановки и выполнила код очистки до завершения скрипта.

Это профессиональная привычка при написании скриптов: если скрипт создаёт временное состояние, он должен также определять, как это состояние удалить. trap — один из основных инструментов Bash для такой задачи.

Итоги

В этой лабораторной работе вы научились создавать надёжные скрипты с помощью Trap.

  1. Сигналы: вы узнали, что Ctrl+C отправляет процессу сигнал SIGINT.
  2. Trap: вы использовали trap для перехвата сигналов и выполнения определённого кода.
  3. Очистка: вы автоматизировали служебные операции, чтобы поддерживать систему в чистом состоянии.

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