#!/bin/bash export LANG=C.UTF-8 # ==AUTOEXEC== # === SD BackUp === # Резервное копирование SD карты # Расширенный скрипт резервного копирования SD # с автоустановкой зависимостей, логированием и CLI-интерфейсом # VERSION="1.2" SCRIPT_NAME="backup-sd" if [[ "$1" == "--help" || "$1" == "-h" ]]; then echo "📘 Использование: $SCRIPT_NAME [опции]" echo echo "Опции:" echo " --comment=… Комментарий для имени файла" echo " --compress=TYPE Сжатие: gzip, xz, zstd, none" echo " --shrink / --no-shrink Уменьшить образ / не уменьшать" echo " --sha256 / --no-sha256 Хэш SHA256 / не считать" echo " --device=/dev/… Устройство (например, /dev/mmcblk0)" echo " --source=/path.img Использовать готовый образ" echo " --keep-img Не удалять .img после shrink" echo " --keep-days=N Удалить копии старше N дней" echo " --keep-count=N Хранить не более N копий" echo " --log=/path.log Путь к лог-файлу" echo " --template=ШАБЛОН Имя: %DATE%, %COMMENT%" echo " -n / --non-interactive Без меню (только аргументы)" echo " -y / --auto-install Установить зависимости" echo " -h / --help Показать эту справку" echo " --version Показать версию скрипта" exit 0 fi if [[ "$1" == "--version" ]]; then echo "$SCRIPT_NAME v$VERSION" exit 0 fi # Определение директории логов if [[ -d /mnt/usb ]]; then LOG_DIR="/mnt/usb/logs/backup" elif [[ -d /tmp ]]; then LOG_DIR="/tmp/backup-logs" else LOG_DIR="/var/log/backup" fi mkdir -p "$LOG_DIR" log() { echo "[$(date +'%F %T')] $1" | tee -a "$LOG_DIR/backup.log" } # ... остальной скрипт будет дополнен ниже # Параметры и аргументы CONFIG_FILE="./backup.conf" USE_CONFIG=false REQUIRED_CMDS=(dd gzip xz zstd losetup parted e2fsck resize2fs tune2fs kpartx mount sha256sum) INSTALL=false NON_INTERACTIVE=false KEEP_DAYS=0 KEEP_COUNT=0 TEMPLATE="" KEEP_IMG=false source_img="" for arg in "$@"; do case $arg in --comment=*) comment="${arg#*=}" ;; --compress=*) compression="${arg#*=}" ;; --shrink) shrink=true ;; --no-shrink) shrink=false ;; --sha256) sha256=true ;; --no-sha256) sha256=false ;; --device=*) sd_card="${arg#*=}" ;; --keep-days=*) KEEP_DAYS="${arg#*=}" ;; --keep-count=*) KEEP_COUNT="${arg#*=}" ;; --log=*) LOG_FILE="${arg#*=}" ;; --template=*) TEMPLATE="${arg#*=}" ;; --keep-img) KEEP_IMG=true ;; --source=*) source_img="${arg#*=}" ;; -n|--non-interactive) NON_INTERACTIVE=true ;; -y|--auto-install) INSTALL=true ;; esac done if $INSTALL; then MISSING=() for cmd in "${REQUIRED_CMDS[@]}"; do if ! command -v "$cmd" &>/dev/null; then MISSING+=("$cmd"); fi done if [[ ${#MISSING[@]} -gt 0 ]]; then log "📦 Устанавливаю зависимости: ${MISSING[*]}" sudo apt-get update -y && sudo apt-get install -y "${MISSING[@]}" else log "✅ Все зависимости уже установлены." fi fi if [[ -f "$CONFIG_FILE" ]]; then source "$CONFIG_FILE" if [[ "$use_config" == "true" ]]; then log "📝 Используем параметры из конфиг-файла $CONFIG_FILE" USE_CONFIG=true fi fi if [[ "$USE_CONFIG" == "false" ]]; then backup_dir="/mnt/backup" temp_dir="/mnt/backup/tmp" sd_card="/dev/mmcblk0" comment="Резервная копия" compression="gzip" shrink=true sha256=true fi if [[ "$NON_INTERACTIVE" == false ]]; then echo "=== Меню резервного копирования ===" echo "1. Снимок - dd + gzip" echo "2. Снимок - dd без сжатия" echo "3. Снимок - dd + shrink" echo "4. Снимок - dd + shrink + gzip" echo "5. Снимок - dd + shrink + xz" echo "6. Уменьшить существующий образ" echo "7. Уменьшить и сжать существующий образ (gzip)" echo "8. Уменьшить и сжать существующий образ (zstd)" echo "0. Выход" read -p "Выберите режим: " CHOICE case "$CHOICE" in 1) compression="gzip"; shrink=false;; 2) compression="none"; shrink=false;; 3) compression="none"; shrink=true;; 4) compression="gzip"; shrink=true;; 5) compression="xz"; shrink=true;; 6) read -p "Путь к существующему .img: " source_img; shrink=true; compression="none";; 7) read -p "Путь к существующему .img: " source_img; shrink=true; compression="gzip";; 8) read -p "Путь к существующему .img: " source_img; shrink=true; compression="zstd";; 0) echo "🚪 Выход"; exit 0;; *) echo "❌ Неверный выбор"; exit 1;; esac [[ -z "$source_img" ]] && read -p "Комментарий к резервной копии: " comment fi mkdir -p "$backup_dir" "$temp_dir" if [[ -z "$source_img" ]]; then log "🔕 Отключаем системное логирование на время копирования" sudo systemctl stop systemd-journald.socket sudo systemctl stop systemd-journald.service fi date_now=$(date +"%Y-%m-%d_%H-%M-%S") comment_safe=$(echo "$comment" | tr ' ' '_' | tr -cd '[:alnum:]_') basename=${TEMPLATE:-backup_%DATE%_%COMMENT%} basename=$(echo "$basename" | sed "s/%DATE%/$date_now/g" | sed "s/%COMMENT%/$comment_safe/g" | cut -c1-100) img_file="$temp_dir/${basename}.img" if [[ -n "$source_img" ]]; then log "📂 Используем существующий файл: $source_img" img_file="$source_img" else log "💾 Создаём образ SD: $img_file" sudo dd if="$sd_card" bs=4M status=progress of="$img_file" fi if [[ ! -f "$img_file" ]]; then log "❌ Файл не найден: $img_file"; exit 1; fi if [[ "$shrink" == "true" ]]; then log "🔧 Уменьшение через pishrink.sh..." [[ ! -x ./pishrink.sh ]] && wget -q https://raw.githubusercontent.com/Drewsif/pishrink/master/pishrink.sh -O ./pishrink.sh && chmod +x ./pishrink.sh keep_flag="" [[ "$KEEP_IMG" == true ]] && keep_flag="-k" sudo ./pishrink.sh $keep_flag "$img_file" fi final_file="$img_file" if [[ "$compression" == "gzip" ]]; then log "📦 Сжатие gzip..." command -v pigz &>/dev/null && pigz -9 "$final_file" || gzip -9 "$final_file" final_file="${final_file}.gz" elif [[ "$compression" == "xz" ]]; then log "📦 Сжатие xz..." xz -T0 -z "$final_file" final_file="${final_file}.xz" elif [[ "$compression" == "zstd" ]]; then log "📦 Сжатие zstd..." zstd -19 -T0 "$final_file" final_file="${final_file}.zst" fi [[ $KEEP_DAYS -gt 0 ]] && find "$backup_dir" -name "backup_*.img*" -type f -mtime +$KEEP_DAYS -exec rm -v {} + [[ $KEEP_COUNT -gt 0 ]] && ls -tp "$backup_dir"/backup_*.img* 2>/dev/null | grep -v '/$' | tail -n +$((KEEP_COUNT + 1)) | xargs -r rm -v if [[ "$sha256" == "true" ]]; then log "🔐 SHA256-сумма..." sha256_value=$(sha256sum "$final_file") echo "$sha256_value" > "$final_file.sha256" fi [[ -n "$LOG_FILE" ]] && echo "[$(date +'%F %T')] File: $final_file | Size: $(du -h "$final_file" | cut -f1) | SHA: $(echo "$sha256_value" | cut -d ' ' -f1)" >> "$LOG_FILE" final_dest="$backup_dir/$(basename "$final_file")" log "📁 Перемещаем результат в каталог: $backup_dir" mv "$final_file" "$final_dest" [[ "$sha256" == "true" && -f "$final_file.sha256" ]] && mv "$final_file.sha256" "$final_dest.sha256" log "🎉 Готово: $final_dest" if [[ -z "$source_img" ]]; then log "🔔 Включаем обратно системное логирование" sudo systemctl start systemd-journald.socket sudo systemctl start systemd-journald.service fi exit 0