Scripts y automatización de archivos

Crear una copia de seguridad ZIP con fecha y verificar lo archivado

Crea un archivo ZIP con fecha a partir de una carpeta sin modificar los originales y verifica después que todos los archivos de origen aparezcan en el archivo con el mismo contenido. Una pequeña carpeta sintética permite comprobar manualmente las rutas, tamaños y totales esperados.

Ver el índice

La traducción se ha realizado con IA. Comprueba el código, las unidades y los valores junto con el original. La revisión por hablantes nativos de cada idioma aún no se ha completado. English

Para quién esEsta guía está dirigida a personas que quieren una copia de seguridad sencilla de una carpeta con fecha y un paso explícito de verificación, en lugar de asumir que la creación del archivo se realizó correctamente.

Preparación
  • Python 3.12 y un comando de terminal que inicie esa versión.
  • Una carpeta de trabajo donde los scripts puedan crear carpetas dentro de outputs.
  • Permiso de lectura para los archivos de origen y suficiente espacio en disco para el archivo ZIP.
  • Solo se requiere la biblioteca estándar de Python: datetime, hashlib, pathlib y zipfile.

01Definir qué debe contener la copia de seguridad

El script archivará los archivos normales de una carpeta de origen, incluidos los archivos de subcarpetas anidadas. Las rutas dentro del ZIP se guardan relativas a la carpeta de origen, de modo que un archivo como reports/week1.txt permanece como reports/week1.txt en lugar de convertirse en una ruta absoluta del sistema de archivos.

El nombre del archivo contiene la fecha local actual en formato YYYY-MM-DD. Si el ejemplo se ejecuta el 2026-09-20, el nombre esperado es backup_demo_2026-09-20.zip. Si se vuelve a ejecutar el mismo día con el mismo destino, el script se detiene en lugar de sustituir la copia de seguridad anterior.

02Crear una carpeta de origen sintética

El siguiente conjunto de datos es sintético y fue creado específicamente para este artículo. Guarda el script de preparación como create_backup_demo.py. Crea cuatro archivos con contenidos de bytes exactos, incluido un archivo dentro de una carpeta reports anidada.

python
from pathlib import Path

SOURCE = Path("outputs") / "backup_demo"
FILES = {
    "notes.txt": b"alpha\n",
    "reports/week1.txt": b"beta\n",
    "reports/week2.txt": b"gamma\n",
    "settings.ini": b"mode=test\n",
}

SOURCE.parent.mkdir(parents=True, exist_ok=True)
SOURCE.mkdir()  # Stop if the synthetic source already exists.

for relative_name, content in FILES.items():
    target = SOURCE / relative_name
    target.parent.mkdir(parents=True, exist_ok=True)
    with target.open("xb") as stream:
        stream.write(content)
text
python create_backup_demo.py
Ruta relativaContenido exactoBytes
notes.txtalpha más salto de línea6
reports/week1.txtbeta más salto de línea5
reports/week2.txtgamma más salto de línea6
settings.inimode=test más salto de línea10

El origen contiene 4 archivos normales con un total de 27 bytes de contenido sin comprimir: 6 + 5 + 6 + 10. El tamaño final del propio archivo ZIP no puede predecirse a partir de ese total porque la compresión ZIP y los metadatos del archivo añaden su propia sobrecarga.

03Preparar la ejecución de la copia de seguridad con fecha

  1. Guarda el script principal como dated_zip_backup.py junto al script de preparación.
  2. Mantén SOURCE como outputs/backup_demo para este ejemplo.
  3. Mantén BACKUP_DIR como outputs/backups para que el archivo quede fuera de la carpeta de origen.
  4. Ejecuta el script desde el mismo directorio de trabajo.
text
python dated_zip_backup.py

El script comprueba que el origen exista y que el directorio de copia de seguridad no esté dentro del directorio de origen. La carpeta padre de la copia de seguridad puede existir previamente. El archivo ZIP con fecha no debe existir.

04Crear el ZIP y verificar cada miembro

El script crea primero una lista ordenada de archivos de origen. Crea el archivo mediante creación exclusiva, escribe cada archivo con compresión ZIP_DEFLATED y después vuelve a abrir el archivo para verificarlo. La verificación compara la lista de miembros prevista, comprueba la integridad CRC con testzip y compara los hashes SHA-256 de los archivos de origen y archivados.

python
from datetime import date
import hashlib
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

SOURCE = Path("outputs") / "backup_demo"
BACKUP_DIR = Path("outputs") / "backups"
CHUNK_SIZE = 1024 * 1024


def hash_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        while chunk := stream.read(CHUNK_SIZE):
            digest.update(chunk)
    return digest.hexdigest()


def hash_member(archive: ZipFile, member_name: str) -> str:
    digest = hashlib.sha256()
    with archive.open(member_name, "r") as stream:
        while chunk := stream.read(CHUNK_SIZE):
            digest.update(chunk)
    return digest.hexdigest()


def main() -> None:
    source_root = SOURCE.resolve(strict=True)
    if not source_root.is_dir():
        raise ValueError("SOURCE must be a directory.")

    backup_root = BACKUP_DIR.resolve()
    if backup_root.is_relative_to(source_root):
        raise ValueError("BACKUP_DIR must be outside SOURCE.")

    files = sorted(
        path for path in source_root.rglob("*")
        if path.is_file() and not path.is_symlink()
    )
    if not files:
        raise ValueError("SOURCE contains no regular files to back up.")

    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    archive_name = f"{source_root.name}_{date.today().isoformat()}.zip"
    archive_path = BACKUP_DIR / archive_name
    if archive_path.exists():
        raise FileExistsError(f"Backup already exists: {archive_path}")

    expected = {}
    with archive_path.open("xb") as raw:
        with ZipFile(raw, "w", compression=ZIP_DEFLATED) as archive:
            for path in files:
                relative = path.relative_to(source_root).as_posix()
                expected[relative] = (path.stat().st_size, hash_file(path))
                archive.write(path, arcname=relative)

    with ZipFile(archive_path, "r") as archive:
        bad_member = archive.testzip()
        if bad_member is not None:
            raise RuntimeError(f"CRC check failed: {bad_member}")

        members = [info for info in archive.infolist() if not info.is_dir()]
        actual_names = [info.filename for info in members]
        expected_names = list(expected)
        if actual_names != expected_names:
            raise RuntimeError("Archive member list does not match the source list.")

        for info in members:
            expected_size, expected_hash = expected[info.filename]
            if info.file_size != expected_size:
                raise RuntimeError(f"Size mismatch: {info.filename}")
            if hash_member(archive, info.filename) != expected_hash:
                raise RuntimeError(f"Hash mismatch: {info.filename}")

    total_bytes = sum(size for size, _ in expected.values())
    print(f"Verified {len(expected)} files totaling {total_bytes} bytes.")
    print(f"Archive: {archive_path.as_posix()}")


if __name__ == "__main__":
    main()

El código omite los enlaces simbólicos y archiva únicamente archivos normales. Los hashes esperados se calculan a partir del origen inmediatamente antes de añadir cada archivo. En una carpeta local normal que permanezca sin cambios durante la ejecución, esto proporciona una comparación útil a nivel de contenido después de crear el archivo.

05Comprobar manualmente el archivo esperado

Si el script se ejecuta el 2026-09-20, la ruta esperada del archivo es outputs/backups/backup_demo_2026-09-20.zip. El ZIP debería contener exactamente las siguientes cuatro rutas de miembros.

Miembro del archivoBytes sin comprimir
notes.txt6
reports/week1.txt5
reports/week2.txt6
settings.ini10

La carpeta reports anidada está representada por las propias rutas de los miembros. El script no necesita una entrada de directorio independiente para reports porque los archivos ya incluyen esa ruta relativa.

La salida de consola esperada que aparece a continuación se obtuvo manualmente para una ejecución el 2026-09-20. No es un registro capturado de ejecución.

text
Verified 4 files totaling 27 bytes.
Archive: outputs/backups/backup_demo_2026-09-20.zip

06Realizar algunas comprobaciones independientes

  1. Abre el ZIP con un visor de archivos comprimidos y confirma que haya exactamente 4 archivos con las rutas relativas esperadas.
  2. Extrae el archivo en una ubicación temporal independiente y compara el contenido de texto con los 4 archivos de origen.
  3. Confirma que los archivos originales sigan existiendo en outputs/backup_demo después de completar la copia de seguridad.
  4. Ejecuta de nuevo el script de copia de seguridad en la misma fecha sin cambiar el destino. Debería detenerse con FileExistsError en lugar de sustituir el archivo existente.
  5. Si quieres confirmar que la verificación sigue el contenido nuevo, modifica una copia de un archivo de origen en una prueba separada y crea una nueva copia de seguridad con un nombre de archivo distinto.

No utilices el tamaño del archivo comprimido como comprobación principal. Debido a la compresión, un archivo válido puede ser menor o mayor que el total sin comprimir del origen, según los datos y los metadatos.

07Reconocer errores comunes

SíntomaQué comprobar
FileNotFoundErrorComprueba SOURCE y el directorio de trabajo del terminal. Para este ejemplo, ejecuta primero el script de preparación sintético.
FileExistsErrorYa existe una copia de seguridad con el mismo nombre fechado. Revísala y elige otro destino o espera a otra fecha de copia prevista en lugar de sobrescribirla.
PermissionErrorConfirma que el script pueda leer todos los archivos de origen y crear archivos dentro de BACKUP_DIR.
CRC check failedConsidera el archivo como fallido. No dependas de él como copia de seguridad; investiga posibles errores de almacenamiento o escritura y crea un archivo nuevo.
Member list mismatchEs posible que el origen haya cambiado durante la ejecución o que el contenido del archivo no coincida con la lista de archivos creada al principio.
Hash or size mismatchConsidera que la verificación ha fallado. Mantén el origen sin cambios e investiga la discrepancia antes de crear un nuevo archivo.

Si se produce una excepción después de crear el archivo ZIP, puede quedar en la carpeta de copias un archivo parcial o no verificado. El script no lo elimina automáticamente porque una limpieza automática puede ocultar información útil para diagnosticar el fallo.

08Comprender lo que esta copia de seguridad no garantiza

Este es un flujo de archivado mediante copia de archivos, no una instantánea transaccional del sistema de archivos. Si otra aplicación modifica un archivo mientras se calcula su hash o se archiva, el resultado puede quedar inconsistente. Para datos importantes, detén las escrituras en el origen o utiliza un sistema de copia de seguridad con capacidad de instantáneas.

El ejemplo no conserva todas las propiedades del sistema de archivos. Los permisos, la propiedad, las listas de control de acceso, los atributos extendidos, los flujos de datos alternativos, los enlaces simbólicos y los metadatos específicos de aplicaciones pueden requerir otra herramienta de copia de seguridad. ZIP es útil para colecciones de archivos portátiles, pero no es una imagen completa de un sistema de archivos.

Un archivo verificado sigue siendo una sola copia. Un plan de copia de seguridad real debería considerar múltiples copias, fallos de almacenamiento, eliminación accidental, ransomware, periodos de retención, cifrado y si la recuperación se ha probado realmente. La verificación aquí muestra que este archivo coincidía con el contenido del origen observado durante esta ejecución; no establece una estrategia completa de recuperación ante desastres.

Registro de ejecución y verificación

2026-09-20 · ejemplo revisado manualmente · objetivo: Python 3.12 · biblioteca estándar: datetime, hashlib, pathlib, zipfile · sin ejecución

  • Se comprobaron manualmente los tamaños de los cuatro archivos sintéticos como 6, 5, 6 y 10 bytes.
  • Se calculó manualmente el total sin comprimir del origen como 27 bytes.
  • Se enumeraron manualmente las rutas esperadas de los miembros del archivo, incluidas las dos rutas anidadas de reports.
  • Se revisó el código para confirmar que los archivos de origen se leen, el ZIP se escribe dentro de outputs y una copia con la misma fecha ya existente hace que la ejecución se detenga.
  • Se revisó la lógica de verificación para las pruebas CRC, la comparación de la lista de miembros, la comparación del tamaño sin comprimir y la comparación SHA-256.
  • Se obtuvieron manualmente el nombre esperado del archivo para 2026-09-20 y la salida de consola.
Límites de la verificación
  • El código no fue ejecutado por el autor de esta respuesta; no se creó ni se abrió ningún archivo ZIP.
  • Los valores SHA-256 y los resultados CRC no se calcularon manualmente porque la lógica de comparación es la comprobación relevante para este ejemplo.
  • No se probaron cambios simultáneos en el origen, errores de permisos, corrupción de almacenamiento, escrituras interrumpidas, enlaces simbólicos ni rendimiento con directorios grandes.
  • No se predijo el tamaño comprimido del archivo porque depende del resultado de la compresión y de los metadatos ZIP.
  • Las URL de la documentación oficial se proporcionaron a partir de ubicaciones de documentación conocidas, pero no se comprobaron en línea.

Criterios de redacción y verificación de todo el sitio

Fuentes de referencia

Las explicaciones y los ejemplos son de elaboración propia. Puedes consultar los comportamientos y conceptos relacionados en las siguientes fuentes oficiales.