Бэкап Docker Compose: автоматизация через Bash и Crontab

Золотые правила автообновления Docker
За 20 лет работы я усвоил железное правило: данные без бэкапа — это данные, которых уже нет. Сегодня разберём, как правильно бэкапить Docker Compose приложения с помощью Bash скриптов и Crontab. Никаких платных решений — только открытый код и проверенные временем инструменты.

Что нужно бэкапить в Docker Compose инфраструктуре

Когда новички думают о бэкапе Docker контейнеров, они часто забывают о критичных компонентах. Вот полный список того, что должно попасть в резервную копию:

  • Docker volumes — здесь живут ваши базы данных, загруженные файлы, конфигурации
  • Файлы docker-compose.yml и .env — конфигурация всей инфраструктуры
  • Bind mounts — примонтированные директории с хоста
  • Образы контейнеров (опционально) — если используете кастомные образы
  • Сетевые настройки — для сложных конфигураций

Забудьте про бэкап самих контейнеров — это бессмысленно. Контейнеры эфемерны, важны только данные и конфигурация.

Пример 1: Базовый скрипт бэкапа Docker Compose

Начнём с простого, но функционального скрипта, который покрывает 80% задач. Этот скрипт я использую для своих домашних проектов уже 3 года — ни одного сбоя.


#!/bin/bash
# Файл: /opt/scripts/docker-backup-basic.sh
# Базовый скрипт бэкапа Docker Compose

set -euo pipefail  # Прерывать при ошибках

# ============= КОНФИГУРАЦИЯ =============
COMPOSE_DIR="/opt/myapp"
BACKUP_ROOT="/backups/docker"
BACKUP_RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_ROOT}/${DATE}"

# ============= СОЗДАНИЕ ДИРЕКТОРИИ =============
mkdir -p "$BACKUP_DIR"

echo "$(date) - Начало бэкапа в $BACKUP_DIR"

# ============= БЭКАП КОНФИГУРАЦИИ =============
echo "Сохранение docker-compose.yml и .env файлов..."
cd "$COMPOSE_DIR" || exit 1
cp docker-compose.yml "$BACKUP_DIR/"
[ -f .env ] && cp .env "$BACKUP_DIR/"

# Сохранение списка запущенных контейнеров
docker-compose ps > "$BACKUP_DIR/containers-state.txt"

# ============= БЭКАП VOLUMES =============
echo "Бэкап Docker volumes..."

# Получаем список всех volumes проекта
VOLUMES=$(docker volume ls --format '{{.Name}}' | grep "$(basename $COMPOSE_DIR)")

for volume in $VOLUMES; do
    echo "Бэкапим volume: $volume"
    docker run --rm \
        -v "${volume}:/source:ro" \
        -v "${BACKUP_DIR}:/backup" \
        alpine \
        tar czf "/backup/${volume}.tar.gz" -C /source .
done

# ============= БЭКАП BIND MOUNTS =============
echo "Бэкап bind mounts..."
if [ -d "${COMPOSE_DIR}/data" ]; then
    tar czf "${BACKUP_DIR}/bind-mounts.tar.gz" -C "${COMPOSE_DIR}" data
fi

# ============= СОЗДАНИЕ АРХИВА =============
echo "Создание итогового архива..."
cd "$BACKUP_ROOT" || exit 1
tar czf "${DATE}.tar.gz" "${DATE}"
rm -rf "${DATE}"  # Удаляем временную директорию

# ============= ОЧИСТКА СТАРЫХ БЭКАПОВ =============
echo "Удаление бэкапов старше ${BACKUP_RETENTION_DAYS} дней..."
find "$BACKUP_ROOT" -name "*.tar.gz" -type f -mtime +${BACKUP_RETENTION_DAYS} -delete

echo "$(date) - Бэкап завершён: ${BACKUP_ROOT}/${DATE}.tar.gz"
echo "Размер: $(du -h ${BACKUP_ROOT}/${DATE}.tar.gz | cut -f1)"

Установка и запуск базового скрипта


# Создаём директории
mkdir -p /opt/scripts /backups/docker

# Сохраняем скрипт
nano /opt/scripts/docker-backup-basic.sh
# Вставляем код выше

# Делаем исполняемым
chmod +x /opt/scripts/docker-backup-basic.sh

# Тестовый запуск
/opt/scripts/docker-backup-basic.sh

# Проверяем результат
ls -lh /backups/docker/

Пример 2: Продвинутый скрипт с остановкой контейнеров

Для баз данных (PostgreSQL, MySQL, MongoDB) простого копирования volumes недостаточно — можно получить повреждённые данные. Правильный способ — остановить контейнеры или использовать нативные инструменты дампа.


#!/bin/bash
# Файл: /opt/scripts/docker-backup-advanced.sh
# Продвинутый скрипт с остановкой контейнеров и дампом БД

set -euo pipefail

# ============= КОНФИГУРАЦИЯ =============
COMPOSE_DIR="/opt/myapp"
BACKUP_ROOT="/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_ROOT}/${DATE}"
LOG_FILE="/var/log/docker-backup.log"

# Функция логирования
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# ============= СОЗДАНИЕ СТРУКТУРЫ =============
mkdir -p "$BACKUP_DIR"/{volumes,configs,dumps}

log "=== Начало бэкапа ==="

# ============= ПРОВЕРКА DOCKER =============
if ! docker ps > /dev/null 2>&1; then
    log "ERROR: Docker недоступен!"
    exit 1
fi

cd "$COMPOSE_DIR" || exit 1

# ============= БЭКАП КОНФИГУРАЦИЙ =============
log "Сохранение конфигураций..."
cp docker-compose.yml "$BACKUP_DIR/configs/"
[ -f .env ] && cp .env "$BACKUP_DIR/configs/"
docker-compose config > "$BACKUP_DIR/configs/docker-compose-resolved.yml"

# ============= ДАМП POSTGRESQL =============
log "Создание дампа PostgreSQL..."
docker-compose exec -T postgres pg_dumpall -U postgres | \
    gzip > "$BACKUP_DIR/dumps/postgres-dump.sql.gz"

# Или для конкретной БД
# docker-compose exec -T postgres pg_dump -U postgres mydb | \
#     gzip > "$BACKUP_DIR/dumps/mydb.sql.gz"

# ============= ДАМП MYSQL =============
log "Создание дампа MySQL..."
docker-compose exec -T mysql mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" --all-databases | \
    gzip > "$BACKUP_DIR/dumps/mysql-dump.sql.gz"

# ============= ДАМП MONGODB =============
log "Создание дампа MongoDB..."
docker-compose exec -T mongodb mongodump --archive --gzip | \
    cat > "$BACKUP_DIR/dumps/mongodb-dump.archive.gz"

# ============= ОСТАНОВКА КОНТЕЙНЕРОВ =============
log "Остановка контейнеров для консистентного бэкапа..."
docker-compose stop

# ============= БЭКАП VOLUMES =============
log "Бэкап всех volumes..."
VOLUMES=$(docker volume ls --format '{{.Name}}' | grep "$(basename $COMPOSE_DIR)")

for volume in $VOLUMES; do
    log "Бэкап volume: $volume"
    docker run --rm \
        -v "${volume}:/source:ro" \
        -v "${BACKUP_DIR}/volumes:/backup" \
        alpine \
        tar czf "/backup/${volume}.tar.gz" -C /source .
done

# ============= ЗАПУСК КОНТЕЙНЕРОВ =============
log "Запуск контейнеров обратно..."
docker-compose start

# Ждём готовности
sleep 10

# Проверяем, что всё запустилось
if docker-compose ps | grep -q "Exit"; then
    log "WARNING: Некоторые контейнеры не запустились!"
fi

# ============= СОЗДАНИЕ ФИНАЛЬНОГО АРХИВА =============
log "Создание итогового архива..."
cd "$BACKUP_ROOT" || exit 1
tar czf "${DATE}.tar.gz" "${DATE}"
BACKUP_SIZE=$(du -h "${DATE}.tar.gz" | cut -f1)
rm -rf "${DATE}"

# ============= ОЧИСТКА СТАРЫХ БЭКАПОВ =============
log "Очистка старых бэкапов (>30 дней)..."
find "$BACKUP_ROOT" -name "*.tar.gz" -type f -mtime +30 -delete

log "=== Бэкап завершён успешно ==="
log "Файл: ${BACKUP_ROOT}/${DATE}.tar.gz"
log "Размер: ${BACKUP_SIZE}"

Пример 3: Скрипт бэкапа без остановки контейнеров

Для production систем, которые нельзя останавливать, используем «горячий» бэкап через нативные инструменты баз данных без downtime.


#!/bin/bash
# Файл: /opt/scripts/docker-backup-hot.sh
# Горячий бэкап без остановки контейнеров

set -euo pipefail

COMPOSE_DIR="/opt/myapp"
BACKUP_ROOT="/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_ROOT}/${DATE}"

mkdir -p "$BACKUP_DIR"/{dumps,configs}

echo "$(date) - Горячий бэкап без остановки сервисов"

cd "$COMPOSE_DIR" || exit 1

# ============= КОНФИГУРАЦИИ =============
cp docker-compose.yml .env "$BACKUP_DIR/configs/" 2>/dev/null || true

# ============= POSTGRESQL HOT BACKUP =============
# Используем pg_dump вместо копирования файлов
docker-compose exec -T postgres pg_dump -Fc -U postgres mydb \
    > "$BACKUP_DIR/dumps/postgres-mydb.dump"

# Или все базы
docker-compose exec -T postgres pg_dumpall -U postgres \
    | gzip > "$BACKUP_DIR/dumps/postgres-all.sql.gz"

# ============= MYSQL HOT BACKUP =============
docker-compose exec -T mysql sh -c \
    'mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction --quick --all-databases' \
    | gzip > "$BACKUP_DIR/dumps/mysql-all.sql.gz"

# ============= MONGODB HOT BACKUP =============
docker-compose exec -T mongodb mongodump --archive --gzip \
    > "$BACKUP_DIR/dumps/mongodb.archive.gz"

# ============= REDIS SNAPSHOT =============
# Форсируем сохранение и копируем RDB файл
docker-compose exec -T redis redis-cli BGSAVE
sleep 5

docker-compose exec -T redis sh -c 'cat /data/dump.rdb' \
    > "$BACKUP_DIR/dumps/redis-dump.rdb"

# ============= БЭКАП UPLOADED FILES =============
# Если есть volume с файлами пользователей
docker run --rm \
    -v myapp_uploads:/source:ro \
    -v "${BACKUP_DIR}:/backup" \
    alpine \
    tar czf /backup/uploads.tar.gz -C /source .

# ============= АРХИВИРОВАНИЕ =============
cd "$BACKUP_ROOT"
tar czf "${DATE}.tar.gz" "${DATE}"
rm -rf "${DATE}"

echo "$(date) - Бэкап завершён: ${BACKUP_ROOT}/${DATE}.tar.gz"

Пример 4: Скрипт с отправкой бэкапа в облако

Локальные бэкапы — это хорошо, но что если сгорит сервер? Правильный подход — дублировать бэкапы в облако. Покажу интеграцию с популярными сервисами.


#!/bin/bash
# Файл: /opt/scripts/docker-backup-cloud.sh
# Бэкап с отправкой в облако (AWS S3, Backblaze B2, rsync)

set -euo pipefail

# ============= КОНФИГУРАЦИЯ =============
COMPOSE_DIR="/opt/myapp"
BACKUP_ROOT="/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_ROOT}/${DATE}.tar.gz"

# Настройки облачных хранилищ
S3_BUCKET="s3://my-backups-bucket/docker/"
B2_BUCKET="b2://my-backup-bucket/docker/"
REMOTE_SERVER="backup@backup-server.com:/backups/docker/"

# Telegram уведомления
TELEGRAM_BOT_TOKEN="your-bot-token"
TELEGRAM_CHAT_ID="your-chat-id"

# ============= ФУНКЦИИ =============
send_telegram() {
    curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
        -d chat_id="${TELEGRAM_CHAT_ID}" \
        -d text="$1" > /dev/null
}

# ============= СОЗДАНИЕ БЭКАПА =============
echo "$(date) - Создание локального бэкапа..."
send_telegram "🔄 Начало бэкапа Docker на $(hostname)"

mkdir -p "${BACKUP_ROOT}/tmp"
TEMP_DIR="${BACKUP_ROOT}/tmp/${DATE}"
mkdir -p "$TEMP_DIR"

cd "$COMPOSE_DIR" || exit 1

# Конфигурации
cp docker-compose.yml .env "$TEMP_DIR/" 2>/dev/null || true

# Дампы БД
docker-compose exec -T postgres pg_dumpall -U postgres | \
    gzip > "$TEMP_DIR/postgres.sql.gz"

# Volumes
VOLUMES=$(docker volume ls --format '{{.Name}}' | grep "$(basename $COMPOSE_DIR)")
for volume in $VOLUMES; do
    docker run --rm \
        -v "${volume}:/source:ro" \
        -v "${TEMP_DIR}:/backup" \
        alpine \
        tar czf "/backup/${volume}.tar.gz" -C /source .
done

# Создание архива
cd "${BACKUP_ROOT}/tmp"
tar czf "$BACKUP_FILE" "${DATE}"
rm -rf "${TEMP_DIR}"

BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "Локальный бэкап создан: $BACKUP_FILE ($BACKUP_SIZE)"

# ============= ОТПРАВКА В AWS S3 =============
if command -v aws &> /dev/null; then
    echo "Отправка в AWS S3..."
    if aws s3 cp "$BACKUP_FILE" "${S3_BUCKET}" --storage-class STANDARD_IA; then
        echo "✓ Успешно загружено в S3"
        send_telegram "✅ Бэкап загружен в S3: ${BACKUP_SIZE}"
    else
        echo "✗ Ошибка загрузки в S3"
        send_telegram "❌ Ошибка загрузки в S3"
    fi
fi

# ============= ОТПРАВКА В BACKBLAZE B2 =============
if command -v b2 &> /dev/null; then
    echo "Отправка в Backblaze B2..."
    if b2 upload-file my-backup-bucket "$BACKUP_FILE" "docker/${DATE}.tar.gz"; then
        echo "✓ Успешно загружено в B2"
    else
        echo "✗ Ошибка загрузки в B2"
    fi
fi

# ============= ОТПРАВКА НА УДАЛЁННЫЙ СЕРВЕР =============
if command -v rsync &> /dev/null; then
    echo "Отправка на удалённый сервер через rsync..."
    if rsync -avz --progress "$BACKUP_FILE" "$REMOTE_SERVER"; then
        echo "✓ Успешно скопировано на удалённый сервер"
        send_telegram "✅ Бэкап скопирован на backup-server"
    else
        echo "✗ Ошибка копирования на удалённый сервер"
        send_telegram "❌ Ошибка копирования на backup-server"
    fi
fi

# ============= ОЧИСТКА ЛОКАЛЬНЫХ БЭКАПОВ =============
echo "Очистка локальных бэкапов старше 7 дней..."
find "$BACKUP_ROOT" -maxdepth 1 -name "*.tar.gz" -type f -mtime +7 -delete

# Очистка в S3 (оставляем последние 30 дней)
if command -v aws &> /dev/null; then
    aws s3 ls "${S3_BUCKET}" | while read -r line; do
        FILE_DATE=$(echo "$line" | awk '{print $1}')
        FILE_NAME=$(echo "$line" | awk '{print $4}')
        if [ -n "$FILE_DATE" ] && [ -n "$FILE_NAME" ]; then
            AGE_DAYS=$(( ($(date +%s) - $(date -d "$FILE_DATE" +%s)) / 86400 ))
            if [ $AGE_DAYS -gt 30 ]; then
                echo "Удаление старого бэкапа из S3: $FILE_NAME"
                aws s3 rm "${S3_BUCKET}${FILE_NAME}"
            fi
        fi
    done
fi

echo "$(date) - Бэкап завершён успешно"
send_telegram "✅ Бэкап Docker завершён успешно
Размер: ${BACKUP_SIZE}
Сервер: $(hostname)"

Установка AWS CLI для работы с S3


# Установка AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Настройка credentials
aws configure
# AWS Access Key ID: ваш_ключ
# AWS Secret Access Key: ваш_секрет
# Default region name: eu-central-1
# Default output format: json

# Проверка
aws s3 ls

Пример 5: Настройка Crontab для автоматического бэкапа

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


# Открываем crontab для редактирования
crontab -e

# ============= ВАРИАНТ 1: ЕЖЕДНЕВНЫЙ БЭКАП В 3 НОЧИ =============
0 3 * * * /opt/scripts/docker-backup-basic.sh >> /var/log/docker-backup-cron.log 2>&1

# ============= ВАРИАНТ 2: КАЖДЫЕ 6 ЧАСОВ =============
0 */6 * * * /opt/scripts/docker-backup-hot.sh >> /var/log/docker-backup-cron.log 2>&1

# ============= ВАРИАНТ 3: ЕЖЕДНЕВНО + ОБЛАКО В ВОСКРЕСЕНЬЕ =============
# Ежедневный локальный бэкап в 02:00
0 2 * * * /opt/scripts/docker-backup-basic.sh >> /var/log/docker-backup-cron.log 2>&1

# Недельный бэкап в облако каждое воскресенье в 04:00
0 4 * * 0 /opt/scripts/docker-backup-cloud.sh >> /var/log/docker-backup-cron.log 2>&1

# ============= ВАРИАНТ 4: СТРАТЕГИЯ 3-2-1 =============
# 3 копии данных, 2 разных носителя, 1 offsite
# Ежедневно в 01:00 - локальный бэкап
0 1 * * * /opt/scripts/docker-backup-basic.sh >> /var/log/docker-backup-cron.log 2>&1

# Ежедневно в 02:00 - на второй диск
0 2 * * * rsync -a /backups/docker/ /mnt/backup-disk/docker/ >> /var/log/docker-backup-cron.log 2>&1

# Еженедельно в воскресенье 03:00 - в облако
0 3 * * 0 /opt/scripts/docker-backup-cloud.sh >> /var/log/docker-backup-cron.log 2>&1

# ============= ПРОВЕРКА CRONTAB =============
# Список всех задач
crontab -l

# Просмотр логов cron
tail -f /var/log/docker-backup-cron.log

# Или системные логи cron
grep CRON /var/log/syslog | tail -20

Расписание cron: шпаргалка по синтаксису


# Формат: минута час день_месяца месяц день_недели команда
# * * * * * команда
# │ │ │ │ │
# │ │ │ │ └─── день недели (0-7, 0 и 7 = воскресенье)
# │ │ │ └───── месяц (1-12)
# │ │ └─────── день месяца (1-31)
# │ └───────── час (0-23)
# └─────────── минута (0-59)

# Примеры популярных расписаний:

# Каждый день в 02:30
30 2 * * *

# Каждый понедельник в 05:00
0 5 * * 1

# Первого числа каждого месяца в 00:00
0 0 1 * *

# Каждые 12 часов
0 */12 * * *

# Каждые 30 минут
*/30 * * * *

# Рабочие дни (пн-пт) в 18:00
0 18 * * 1-5

# Выходные (сб-вс) в 10:00
0 10 * * 6,7

Продвинутый скрипт восстановления из бэкапа

Бэкап без возможности восстановления — это не бэкап. Вот скрипт для быстрого recovery после сбоя.


#!/bin/bash
# Файл: /opt/scripts/docker-restore.sh
# Восстановление из бэкапа

set -euo pipefail

# ============= ПАРАМЕТРЫ =============
if [ $# -lt 1 ]; then
    echo "Использование: $0 <путь_к_бэкапу.tar.gz>"
    echo "Пример: $0 /backups/docker/20240120_030000.tar.gz"
    exit 1
fi

BACKUP_FILE="$1"
RESTORE_DIR="/opt/restore-$(date +%Y%m%d_%H%M%S)"
COMPOSE_DIR="/opt/myapp"

# ============= ПРОВЕРКИ =============
if [ ! -f "$BACKUP_FILE" ]; then
    echo "ERROR: Файл бэкапа не найден: $BACKUP_FILE"
    exit 1
fi

echo "Восстановление из: $BACKUP_FILE"
echo "Директория восстановления: $RESTORE_DIR"
read -p "Продолжить? (yes/no): " CONFIRM

if [ "$CONFIRM" != "yes" ]; then
    echo "Отменено пользователем"
    exit 0
fi

# ============= РАСПАКОВКА БЭКАПА =============
echo "Распаковка бэкапа..."
mkdir -p "$RESTORE_DIR"
tar xzf "$BACKUP_FILE" -C "$RESTORE_DIR" --strip-components=1

# ============= ОСТАНОВКА ТЕКУЩИХ КОНТЕЙНЕРОВ =============
echo "Остановка текущих контейнеров..."
cd "$COMPOSE_DIR" || exit 1
docker-compose down -v  # -v удалит volumes

# ============= ВОССТАНОВЛЕНИЕ КОНФИГУРАЦИЙ =============
echo "Восстановление конфигураций..."
cp "$RESTORE_DIR"/configs/* "$COMPOSE_DIR/" 2>/dev/null || \
    cp "$RESTORE_DIR"/*.yml "$COMPOSE_DIR/" 2>/dev/null || true

# ============= СОЗДАНИЕ VOLUMES =============
echo "Создание volumes из docker-compose.yml..."
cd "$COMPOSE_DIR"
docker-compose up --no-start

# ============= ВОССТАНОВЛЕНИЕ VOLUMES =============
echo "Восстановление данных volumes..."
for volume_backup in "$RESTORE_DIR"/volumes/*.tar.gz "$RESTORE_DIR"/*.tar.gz; do
    if [ -f "$volume_backup" ]; then
        volume_name=$(basename "$volume_backup" .tar.gz)
        echo "Восстановление volume: $volume_name"
        
        docker run --rm \
            -v "${volume_name}:/target" \
            -v "$RESTORE_DIR":/backup \
            alpine \
            sh -c "cd /target && tar xzf /backup/$(basename $volume_backup)"
    fi
done

# ============= ВОССТАНОВЛЕНИЕ ДАМПОВ БД =============
if [ -d "$RESTORE_DIR/dumps" ]; then
    echo "Запуск контейнеров для восстановления БД..."
    docker-compose up -d
    
    # Ждём готовности БД
    echo "Ожидание готовности баз данных..."
    sleep 15
    
    # PostgreSQL
    if [ -f "$RESTORE_DIR/dumps/postgres-dump.sql.gz" ]; then
        echo "Восстановление PostgreSQL..."
        gunzip < "$RESTORE_DIR/dumps/postgres-dump.sql.gz" | \
            docker-compose exec -T postgres psql -U postgres
    fi
    
    # MySQL
    if [ -f "$RESTORE_DIR/dumps/mysql-dump.sql.gz" ]; then
        echo "Восстановление MySQL..."
        gunzip < "$RESTORE_DIR/dumps/mysql-dump.sql.gz" | \
            docker-compose exec -T mysql mysql -u root -p"${MYSQL_ROOT_PASSWORD}"
    fi
    
    # MongoDB
    if [ -f "$RESTORE_DIR/dumps/mongodb-dump.archive.gz" ]; then
        echo "Восстановление MongoDB..."
        gunzip < "$RESTORE_DIR/dumps/mongodb-dump.archive.gz" | \
            docker-compose exec -T mongodb mongorestore --archive --drop
    fi
else
    # Если дампов нет, просто запускаем контейнеры
    echo "Запуск контейнеров..."
    docker-compose up -d
fi

# ============= ПРОВЕРКА =============
echo ""
echo "============= СТАТУС ВОССТАНОВЛЕНИЯ ============="
docker-compose ps
echo ""
echo "Восстановление завершено!"
echo "Проверьте работоспособность приложения"
echo "Временные файлы в: $RESTORE_DIR"
echo ""
read -p "Удалить временные файлы? (yes/no): " CLEANUP

if [ "$CLEANUP" = "yes" ]; then
    rm -rf "$RESTORE_DIR"
    echo "Временные файлы удалены"
fi

Оставайтесь на связи

Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.

Подписаться на IT-Аптеку →

Мы ВКонтакте

IT-Аптека — советы, новости и помощь рядом.

Вступить в группу ВКонтакте →
Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх