Compresión y Archivado

Aprende a comprimir, archivar y gestionar backups

NIVEL INTERMEDIO

Tipos de Compresión

La compresión reduce el tamaño de archivos y directorios para ahorrar espacio en disco y acelerar transferencias. En Linux tenemos varias herramientas:

Herramientas de Compresión
  • tar: Archiva múltiples archivos en uno solo
  • gzip (.gz): Compresión rápida, buena para archivos de texto
  • bzip2 (.bz2): Mayor compresión que gzip, más lento
  • xz (.xz): Mejor compresión, más lento que bzip2
  • zip: Compatible con Windows, incluye archivado
  • 7z: Excelente compresión, múltiples algoritmos

TAR - Archivado

1. Comandos básicos de tar

#!/bin/bash

# Crear archivo tar
tar -cf archivo.tar directorio/

# Crear tar con compresión gzip
tar -czf archivo.tar.gz directorio/

# Crear tar con compresión bzip2
tar -cjf archivo.tar.bz2 directorio/

# Crear tar con compresión xz
tar -cJf archivo.tar.xz directorio/

# Listar contenido sin extraer
tar -tf archivo.tar.gz

# Extraer archivo tar
tar -xf archivo.tar

# Extraer tar comprimido
tar -xzf archivo.tar.gz

# Extraer a directorio específico
tar -xzf archivo.tar.gz -C /ruta/destino/

# Extraer archivos específicos
tar -xzf archivo.tar.gz archivo1.txt directorio/archivo2.txt

2. Script avanzado de archivado

#!/bin/bash
# advanced_tar.sh - Script avanzado de archivado

create_archive() {
    local source="$1"
    local destination="$2"
    local compression="$3"
    local exclude_pattern="$4"
    local verbose="$5"
    
    if [[ ! -e "$source" ]]; then
        echo "Error: Fuente no existe - $source"
        return 1
    fi
    
    # Configurar opciones de tar
    local tar_opts="-c"
    local extension=""
    
    case "$compression" in
        "gzip"|"gz")
            tar_opts="${tar_opts}z"
            extension=".tar.gz"
            ;;
        "bzip2"|"bz2")
            tar_opts="${tar_opts}j"
            extension=".tar.bz2"
            ;;
        "xz")
            tar_opts="${tar_opts}J"
            extension=".tar.xz"
            ;;
        "none"|"")
            extension=".tar"
            ;;
        *)
            echo "Compresión no soportada: $compression"
            return 1
            ;;
    esac
    
    # Agregar verbosity si se solicita
    if [[ "$verbose" == "true" ]]; then
        tar_opts="${tar_opts}v"
    fi
    
    # Agregar opción de archivo
    tar_opts="${tar_opts}f"
    
    # Construir nombre de archivo de destino
    if [[ -z "$destination" ]]; then
        destination="$(basename "$source")_$(date +%Y%m%d_%H%M%S)${extension}"
    elif [[ ! "$destination" =~ \.(tar|tar\.gz|tar\.bz2|tar\.xz)$ ]]; then
        destination="${destination}${extension}"
    fi
    
    echo "Creando archivo: $destination"
    echo "Fuente: $source"
    echo "Compresión: ${compression:-none}"
    
    # Ejecutar tar con exclusiones si se especifican
    if [[ -n "$exclude_pattern" ]]; then
        tar $tar_opts "$destination" --exclude="$exclude_pattern" "$source"
    else
        tar $tar_opts "$destination" "$source"
    fi
    
    if [[ $? -eq 0 ]]; then
        echo "Archivo creado exitosamente: $destination"
        echo "Tamaño: $(du -h "$destination" | cut -f1)"
        
        # Calcular ratio de compresión si hay compresión
        if [[ "$compression" != "none" && -n "$compression" ]]; then
            local original_size=$(du -sb "$source" | cut -f1)
            local compressed_size=$(stat -f%z "$destination" 2>/dev/null || stat -c%s "$destination" 2>/dev/null)
            local ratio=$((100 - (compressed_size * 100 / original_size)))
            echo "Ratio de compresión: ${ratio}%"
        fi
    else
        echo "Error al crear archivo"
        return 1
    fi
}

extract_archive() {
    local archive="$1"
    local destination="$2"
    local verbose="$3"
    
    if [[ ! -f "$archive" ]]; then
        echo "Error: Archivo no existe - $archive"
        return 1
    fi
    
    # Crear directorio de destino si no existe
    if [[ -n "$destination" ]]; then
        mkdir -p "$destination"
    fi
    
    local tar_opts="-x"
    
    # Detectar tipo de compresión automáticamente
    case "$archive" in
        *.tar.gz|*.tgz)
            tar_opts="${tar_opts}z"
            ;;
        *.tar.bz2|*.tbz2)
            tar_opts="${tar_opts}j"
            ;;
        *.tar.xz|*.txz)
            tar_opts="${tar_opts}J"
            ;;
        *.tar)
            # Sin compresión
            ;;
        *)
            echo "Tipo de archivo no reconocido: $archive"
            return 1
            ;;
    esac
    
    if [[ "$verbose" == "true" ]]; then
        tar_opts="${tar_opts}v"
    fi
    
    tar_opts="${tar_opts}f"
    
    echo "Extrayendo: $archive"
    if [[ -n "$destination" ]]; then
        echo "Destino: $destination"
        tar $tar_opts "$archive" -C "$destination"
    else
        tar $tar_opts "$archive"
    fi
    
    if [[ $? -eq 0 ]]; then
        echo "Extracción completada exitosamente"
    else
        echo "Error durante la extracción"
        return 1
    fi
}

show_archive_info() {
    local archive="$1"
    
    if [[ ! -f "$archive" ]]; then
        echo "Error: Archivo no existe - $archive"
        return 1
    fi
    
    echo "=== Información del Archivo ==="
    echo "Archivo: $archive"
    echo "Tamaño: $(du -h "$archive" | cut -f1)"
    echo "Fecha de modificación: $(stat -f%Sm "$archive" 2>/dev/null || stat -c%y "$archive" 2>/dev/null)"
    echo ""
    echo "=== Contenido del Archivo ==="
    tar -tvf "$archive" | head -20
    
    local total_files=$(tar -tf "$archive" | wc -l)
    echo ""
    echo "Total de archivos: $total_files"
    
    if [[ $total_files -gt 20 ]]; then
        echo "(Mostrando solo los primeros 20 archivos)"
    fi
}

# Función principal con menú interactivo
main() {
    if [[ $# -eq 0 ]]; then
        echo "=== Herramienta de Archivado TAR ==="
        echo "1. Crear archivo"
        echo "2. Extraer archivo" 
        echo "3. Ver información de archivo"
        echo "4. Ayuda"
        
        read -p "Seleccione opción [1-4]: " option
        
        case "$option" in
            1)
                read -p "Ruta a archivar: " source
                read -p "Archivo de destino (opcional): " dest
                read -p "Compresión (gzip/bzip2/xz/none) [gzip]: " comp
                read -p "Patrón de exclusión (opcional): " exclude
                read -p "Modo verboso (y/N): " verbose
                
                comp=${comp:-gzip}
                [[ "$verbose" =~ ^[Yy]$ ]] && verbose="true" || verbose="false"
                
                create_archive "$source" "$dest" "$comp" "$exclude" "$verbose"
                ;;
            2)
                read -p "Archivo a extraer: " archive
                read -p "Directorio de destino (opcional): " dest
                read -p "Modo verboso (y/N): " verbose
                
                [[ "$verbose" =~ ^[Yy]$ ]] && verbose="true" || verbose="false"
                
                extract_archive "$archive" "$dest" "$verbose"
                ;;
            3)
                read -p "Archivo a inspeccionar: " archive
                show_archive_info "$archive"
                ;;
            4)
                echo "Uso directo:"
                echo "$0 create  [destino] [compresión] [exclusión] [verbose]"
                echo "$0 extract  [destino] [verbose]"
                echo "$0 info "
                ;;
        esac
    else
        case "$1" in
            "create")
                create_archive "$2" "$3" "$4" "$5" "$6"
                ;;
            "extract")
                extract_archive "$2" "$3" "$4"
                ;;
            "info")
                show_archive_info "$2"
                ;;
            *)
                echo "Comando no reconocido: $1"
                echo "Comandos disponibles: create, extract, info"
                ;;
        esac
    fi
}

# Ejecutar función principal
main "$@"

Herramientas de Compresión

1. GZIP - Compresión rápida

#!/bin/bash

# Comprimir archivo
gzip archivo.txt  # Crea archivo.txt.gz y elimina original

# Comprimir manteniendo original
gzip -c archivo.txt > archivo.txt.gz

# Descomprimir
gunzip archivo.txt.gz  # Restaura archivo.txt

# Ver contenido sin descomprimir
zcat archivo.txt.gz

# Comprimir con máxima compresión
gzip -9 archivo.txt

# Comprimir múltiples archivos
gzip archivo1.txt archivo2.txt archivo3.txt

2. ZIP - Compatible multiplataforma

#!/bin/bash

# Crear archivo ZIP
zip archivo.zip archivo1.txt archivo2.txt

# Crear ZIP de directorio recursivamente
zip -r directorio.zip directorio/

# Crear ZIP con contraseña
zip -P contraseña archivo.zip archivo.txt

# Agregar archivos a ZIP existente
zip archivo.zip archivo3.txt

# Extraer ZIP
unzip archivo.zip

# Extraer a directorio específico
unzip archivo.zip -d /ruta/destino/

# Listar contenido sin extraer
unzip -l archivo.zip

# Testear integridad
unzip -t archivo.zip

3. Script de compresión inteligente

#!/bin/bash
# smart_compress.sh - Compresión inteligente según tipo de archivo

compress_smart() {
    local source="$1"
    local format="$2"
    local level="$3"
    
    if [[ ! -e "$source" ]]; then
        echo "Error: Fuente no existe - $source"
        return 1
    fi
    
    # Detectar mejor formato si no se especifica
    if [[ -z "$format" ]]; then
        if file "$source" | grep -q "text"; then
            format="gzip"  # Mejor para texto
        elif [[ -d "$source" ]]; then
            format="tar.xz"  # Mejor para directorios
        else
            format="gzip"  # Por defecto
        fi
        echo "Formato detectado automáticamente: $format"
    fi
    
    level=${level:-6}  # Nivel de compresión por defecto
    
    case "$format" in
        "gzip"|"gz")
            if [[ -f "$source" ]]; then
                gzip -c -$level "$source" > "${source}.gz"
            else
                tar -czf "${source}.tar.gz" "$source"
            fi
            ;;
        "bzip2"|"bz2")
            if [[ -f "$source" ]]; then
                bzip2 -c -$level "$source" > "${source}.bz2"
            else
                tar -cjf "${source}.tar.bz2" "$source"
            fi
            ;;
        "xz")
            if [[ -f "$source" ]]; then
                xz -c -$level "$source" > "${source}.xz"
            else
                tar -cJf "${source}.tar.xz" "$source"
            fi
            ;;
        "zip")
            if [[ -f "$source" ]]; then
                zip -$level "${source}.zip" "$source"
            else
                zip -r -$level "${source}.zip" "$source"
            fi
            ;;
        "7z")
            if command -v 7z >/dev/null 2>&1; then
                7z a -mx=$level "${source}.7z" "$source"
            else
                echo "7z no está instalado"
                return 1
            fi
            ;;
        "tar.gz"|"tgz")
            tar -czf "${source}.tar.gz" "$source"
            ;;
        "tar.bz2"|"tbz2")
            tar -cjf "${source}.tar.bz2" "$source"
            ;;
        "tar.xz"|"txz")
            tar -cJf "${source}.tar.xz" "$source"
            ;;
        *)
            echo "Formato no soportado: $format"
            return 1
            ;;
    esac
    
    # Mostrar estadísticas
    local original_size=$(du -sb "$source" | cut -f1)
    local compressed_file=""
    
    case "$format" in
        "gzip"|"gz") compressed_file="${source}.gz" ;;
        "bzip2"|"bz2") compressed_file="${source}.bz2" ;;
        "xz") compressed_file="${source}.xz" ;;
        "zip") compressed_file="${source}.zip" ;;
        "7z") compressed_file="${source}.7z" ;;
        "tar.gz"|"tgz") compressed_file="${source}.tar.gz" ;;
        "tar.bz2"|"tbz2") compressed_file="${source}.tar.bz2" ;;
        "tar.xz"|"txz") compressed_file="${source}.tar.xz" ;;
    esac
    
    if [[ -f "$compressed_file" ]]; then
        local compressed_size=$(stat -f%z "$compressed_file" 2>/dev/null || stat -c%s "$compressed_file" 2>/dev/null)
        local ratio=$((100 - (compressed_size * 100 / original_size)))
        
        echo "=== Estadísticas de Compresión ==="
        echo "Archivo original: $(numfmt --to=iec $original_size)"
        echo "Archivo comprimido: $(numfmt --to=iec $compressed_size)"
        echo "Ratio de compresión: ${ratio}%"
        echo "Archivo creado: $compressed_file"
    fi
}

benchmark_compression() {
    local source="$1"
    
    if [[ ! -e "$source" ]]; then
        echo "Error: Fuente no existe - $source"
        return 1
    fi
    
    echo "=== Benchmark de Compresión para: $source ==="
    
    local formats=("gzip" "bzip2" "xz" "zip")
    
    for format in "${formats[@]}"; do
        echo "Probando $format..."
        local start_time=$(date +%s.%N)
        
        compress_smart "$source" "$format" 6
        
        local end_time=$(date +%s.%N)
        local duration=$(echo "$end_time - $start_time" | bc)
        
        echo "Tiempo: ${duration}s"
        echo ""
    done
}

# Función principal
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    case "$1" in
        "compress")
            compress_smart "$2" "$3" "$4"
            ;;
        "benchmark")
            benchmark_compression "$2"
            ;;
        *)
            echo "Uso: $0 [compress|benchmark]  [formato] [nivel]"
            echo ""
            echo "Formatos soportados: gzip, bzip2, xz, zip, 7z, tar.gz, tar.bz2, tar.xz"
            echo "Niveles de compresión: 1 (rápido) a 9 (máximo)"
            ;;
    esac
fi
Consejos de Rendimiento
  • gzip: Rápido, bueno para archivos que se comprimen/descomprimen frecuentemente
  • bzip2: Mejor compresión que gzip, útil para archivos de almacenamiento
  • xz: Máxima compresión, ideal para distribución y archivado a largo plazo
  • zip: Compatible con Windows, incluye metadata de archivos

Sistema de Backup Automático

#!/bin/bash
# backup_system.sh - Sistema completo de backups automáticos

BACKUP_BASE_DIR="/backups"
CONFIG_FILE="$HOME/.backup_config"
LOG_FILE="/var/log/backup.log"
RETENTION_DAYS=30

# Configuración por defecto
DEFAULT_CONFIG="# Configuración de Backups
BACKUP_SOURCES=('/home/user/Documents' '/etc' '/var/log')
BACKUP_EXCLUDE_PATTERNS=('*.tmp' '*.cache' '.git' 'node_modules')
COMPRESSION='xz'
ENCRYPTION=false
ENCRYPTION_KEY=''
REMOTE_BACKUP=false
REMOTE_HOST=''
REMOTE_PATH=''
EMAIL_NOTIFICATIONS=false
EMAIL_ADDRESS=''
RETENTION_DAYS=30"

# Crear configuración si no existe
if [[ ! -f "$CONFIG_FILE" ]]; then
    echo "$DEFAULT_CONFIG" > "$CONFIG_FILE"
    echo "Archivo de configuración creado en: $CONFIG_FILE"
fi

# Cargar configuración
source "$CONFIG_FILE"

log_message() {
    local level="$1"
    local message="$2"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE"
}

send_notification() {
    local subject="$1"
    local message="$2"
    
    if [[ "$EMAIL_NOTIFICATIONS" == "true" && -n "$EMAIL_ADDRESS" ]]; then
        echo "$message" | mail -s "$subject" "$EMAIL_ADDRESS"
    fi
}

create_backup() {
    local source="$1"
    local backup_name="$(basename "$source")_$(date +%Y%m%d_%H%M%S)"
    local backup_dir="$BACKUP_BASE_DIR/$(date +%Y/%m)"
    
    # Crear directorio de backup
    mkdir -p "$backup_dir"
    
    log_message "INFO" "Iniciando backup de: $source"
    
    # Preparar opciones de exclusión
    local exclude_opts=""
    for pattern in "${BACKUP_EXCLUDE_PATTERNS[@]}"; do
        exclude_opts="$exclude_opts --exclude=$pattern"
    done
    
    # Crear backup según compresión configurada
    local backup_file=""
    case "$COMPRESSION" in
        "gzip"|"gz")
            backup_file="$backup_dir/${backup_name}.tar.gz"
            tar $exclude_opts -czf "$backup_file" "$source"
            ;;
        "bzip2"|"bz2")
            backup_file="$backup_dir/${backup_name}.tar.bz2"
            tar $exclude_opts -cjf "$backup_file" "$source"
            ;;
        "xz")
            backup_file="$backup_dir/${backup_name}.tar.xz"
            tar $exclude_opts -cJf "$backup_file" "$source"
            ;;
        *)
            backup_file="$backup_dir/${backup_name}.tar"
            tar $exclude_opts -cf "$backup_file" "$source"
            ;;
    esac
    
    if [[ $? -eq 0 ]]; then
        log_message "INFO" "Backup creado: $backup_file"
        
        # Encriptar si está habilitado
        if [[ "$ENCRYPTION" == "true" && -n "$ENCRYPTION_KEY" ]]; then
            encrypt_backup "$backup_file"
        fi
        
        # Verificar integridad
        verify_backup "$backup_file"
        
        # Backup remoto si está configurado
        if [[ "$REMOTE_BACKUP" == "true" ]]; then
            sync_to_remote "$backup_file"
        fi
        
        return 0
    else
        log_message "ERROR" "Fallo al crear backup de: $source"
        return 1
    fi
}

encrypt_backup() {
    local backup_file="$1"
    
    if command -v gpg >/dev/null 2>&1; then
        log_message "INFO" "Encriptando backup: $backup_file"
        gpg --batch --yes --passphrase "$ENCRYPTION_KEY" \
            --symmetric --cipher-algo AES256 "$backup_file"
        
        if [[ $? -eq 0 ]]; then
            rm "$backup_file"  # Eliminar versión no encriptada
            log_message "INFO" "Backup encriptado: ${backup_file}.gpg"
        else
            log_message "ERROR" "Fallo al encriptar backup"
        fi
    else
        log_message "WARNING" "GPG no disponible - backup no encriptado"
    fi
}

verify_backup() {
    local backup_file="$1"
    
    log_message "INFO" "Verificando integridad de: $backup_file"
    
    case "$backup_file" in
        *.tar.gz)
            if gzip -t "$backup_file" >/dev/null 2>&1; then
                log_message "INFO" "Verificación exitosa"
            else
                log_message "ERROR" "Backup corrupto: $backup_file"
                return 1
            fi
            ;;
        *.tar.bz2)
            if bzip2 -t "$backup_file" >/dev/null 2>&1; then
                log_message "INFO" "Verificación exitosa"
            else
                log_message "ERROR" "Backup corrupto: $backup_file"
                return 1
            fi
            ;;
        *.tar.xz)
            if xz -t "$backup_file" >/dev/null 2>&1; then
                log_message "INFO" "Verificación exitosa"
            else
                log_message "ERROR" "Backup corrupto: $backup_file"
                return 1
            fi
            ;;
        *.tar)
            if tar -tf "$backup_file" >/dev/null 2>&1; then
                log_message "INFO" "Verificación exitosa"
            else
                log_message "ERROR" "Backup corrupto: $backup_file"
                return 1
            fi
            ;;
    esac
}

sync_to_remote() {
    local backup_file="$1"
    
    if [[ -n "$REMOTE_HOST" && -n "$REMOTE_PATH" ]]; then
        log_message "INFO" "Sincronizando a servidor remoto: $backup_file"
        
        rsync -avz "$backup_file" "${REMOTE_HOST}:${REMOTE_PATH}/"
        
        if [[ $? -eq 0 ]]; then
            log_message "INFO" "Sincronización remota exitosa"
        else
            log_message "ERROR" "Fallo en sincronización remota"
        fi
    fi
}

cleanup_old_backups() {
    log_message "INFO" "Limpiando backups antiguos (>${RETENTION_DAYS} días)"
    
    find "$BACKUP_BASE_DIR" -type f -name "*.tar*" -mtime +$RETENTION_DAYS -delete
    find "$BACKUP_BASE_DIR" -type f -name "*.gpg" -mtime +$RETENTION_DAYS -delete
    
    # Eliminar directorios vacíos
    find "$BACKUP_BASE_DIR" -type d -empty -delete
    
    log_message "INFO" "Limpieza completada"
}

run_backup() {
    log_message "INFO" "=== Iniciando proceso de backup ==="
    
    local failed_backups=0
    local total_backups=0
    
    for source in "${BACKUP_SOURCES[@]}"; do
        if [[ -e "$source" ]]; then
            ((total_backups++))
            if ! create_backup "$source"; then
                ((failed_backups++))
            fi
        else
            log_message "WARNING" "Fuente no existe: $source"
        fi
    done
    
    # Limpiar backups antiguos
    cleanup_old_backups
    
    # Reporte final
    local successful_backups=$((total_backups - failed_backups))
    log_message "INFO" "=== Proceso completado ==="
    log_message "INFO" "Exitosos: $successful_backups, Fallidos: $failed_backups"
    
    # Notificación
    if [[ $failed_backups -eq 0 ]]; then
        send_notification "Backup Exitoso" "Todos los backups ($total_backups) se completaron exitosamente."
    else
        send_notification "Backup con Errores" "$failed_backups de $total_backups backups fallaron. Revisar logs."
    fi
    
    return $failed_backups
}

# Función principal
case "$1" in
    "run")
        run_backup
        ;;
    "test")
        log_message "INFO" "Modo de prueba - configuración actual:"
        echo "Fuentes: ${BACKUP_SOURCES[*]}"
        echo "Compresión: $COMPRESSION"
        echo "Encriptación: $ENCRYPTION"
        echo "Backup remoto: $REMOTE_BACKUP"
        ;;
    "config")
        echo "Editando configuración..."
        ${EDITOR:-nano} "$CONFIG_FILE"
        ;;
    *)
        echo "Sistema de Backup Automático"
        echo "Uso: $0 [run|test|config]"
        echo ""
        echo "  run    - Ejecutar backup completo"
        echo "  test   - Mostrar configuración actual"
        echo "  config - Editar configuración"
        ;;
esac

Ejercicios Prácticos

Ejercicio 1: Comparador de Compresión

Crea un script que:

  • Compare diferentes algoritmos de compresión
  • Mida tiempo de compresión y ratio obtenido
  • Genere reporte con recomendaciones
  • Pruebe con diferentes tipos de archivos
Ejercicio 2: Archivador Inteligente

Desarrolla un sistema que:

  • Archive automáticamente archivos por fecha/tipo
  • Use compresión adaptativa según contenido
  • Mantenga índice de archivos archivados
  • Permita búsqueda en archivos comprimidos
Ejercicio 3: Sistema de Backup Incremental

Implementa un backup que:

  • Haga backups incrementales inteligentes
  • Use hard links para ahorrar espacio
  • Soporte restauración a puntos específicos
  • Incluya verificación de integridad