Временные файлы остаются после ошибки: очистка Bash через trap EXIT

Скрипт раскладывает промежуточные результаты в /tmp/report, а затем удаляет их последней командой. Пока обработка успешна, каталог исчезает. После отказа на середине работы очистка не выполняется. Следующий запуск видит старые файлы и может принять их за свежие. Если два экземпляра стартуют одновременно, общий каталог добавляет ещё и взаимное влияние.

Нужны отдельное рабочее место на каждый запуск и очистка, привязанная к завершению оболочки. mktemp -d создаёт каталог с непредсказуемым именем. trap с событием EXIT регистрирует действие, которое Bash выполнит перед выходом. Важно сохранить статус основной операции до любой команды очистки.

Один владелец временного каталога

Пример создаёт каталог внутри текущей рабочей папки. Запускайте его в отдельном каталоге упражнения. Единственный rm направлен на путь, только что полученный от mktemp, а не на общую папку с чужими данными.

#!/usr/bin/env bash
work=$(mktemp -d ./report-work.XXXXXX) || exit 1
cleanup() {
    local status=$?
    trap - EXIT
    if ! rm -rf -- "$work"; then
        printf 'Не удалось очистить %s\n' "$work" >&2
        if ((status == 0)); then status=1; fi
    fi
    exit "$status"
}
trap cleanup EXIT
printf '%s\n' sample > "$work/input.txt"
printf 'Рабочий каталог: %s\n' "$work"
exit 7

После выполнения каталог должен исчезнуть, а статус скрипта остаться 7. Строка local сохраняет $? до отключения trap и удаления файлов. Если основная операция прошла успешно, но очистка не удалась, пример возвращает 1. Это выбранная политика: неприбранный рабочий каталог тоже виден вызывающему заданию.

Регистрация trap стоит сразу после успешного mktemp. Между созданием ресурса и регистрацией очистки лучше не размещать подготовку, которая сама может завершиться ошибкой. Иначе появится короткая, но реальная ветка с созданным каталогом без обработчика.

Почему действие задаётся функцией

Функция делает порядок операций видимым: сохранение статуса, отключение обработчика, очистка, возврат статуса. В длинной строке trap легко перепутать момент раскрытия переменных. Например, двойные кавычки могут раскрыть $? уже при регистрации, а не при завершении. Здесь cleanup выполняется только при выходе, когда нужен актуальный результат.

Этот скрипт должен запускаться отдельным процессом. Подключать его через source нельзя: exit завершит вызывающую оболочку, а trap заменит её обработчик EXIT. Для библиотечной функции нужен другой контракт владения ресурсами и сохранение предыдущих обработчиков.

trap не гарантирует очистку после SIGKILL, аварии машины или потери питания. Поэтому временные файлы не должны быть единственным местом хранения результата, который требуется сохранить. Старые каталоги при необходимости убирают отдельной процедурой с проверкой владельца и возраста, а не расширением rm до общего шаблона.

Обработчик EXIT сам по себе не останавливает скрипт при ошибке команды. В примере завершение задаётся явным exit 7. При подключении реальной обработки проверьте её результат сразу: при неуспехе сохраните статус и выполните exit с этим значением. Иначе следующая успешная команда изменит итог, а cleanup сохранит уже его.

Что сохранять перед удалением

При ошибке промежуточные файлы иногда нужны для разбора. Тогда заранее решите, какие небольшие диагностические результаты скопировать в постоянный каталог, а что удалить. Секретные данные и большие выгрузки не стоит оставлять случайно только ради отладки. Управляемый режим сохранения диагностики понятнее, чем комментарий к последней строке rm.

Проверьте успешный exit 0, намеренный exit 7 и ошибку команды в основном теле. В каждом случае статус и наличие рабочего каталога должны соответствовать выбранной политике. Только после этого подключайте реальную обработку вместо printf.

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

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.