Una copia automática sirve cuando puedes restaurarla. Vamos a escribir un programa que empaqueta una carpeta, comprueba el ZIP y guarda una huella SHA-256. Funciona para proyectos y configuraciones que no cambian durante la copia. Para un mundo de juego activo o una base de datos, detén el proceso o utiliza su mecanismo de snapshot antes de ejecutar el script.
Última revisión: 2 de octubre de 2026. Probado con: Python 3.14.4 en Windows. El ejemplo utiliza Path.walk, disponible desde Python 3.12; la documentación consultada pertenece a la rama 3.14. No instala dependencias.
Prepara origen y destino
El destino debe estar fuera del origen. Guardar copias dentro de la carpeta que comprimes produce inclusiones recursivas. Necesitas lectura del origen, escritura del destino y espacio para una copia completa.

Guarda este código como backup.py. Omite .git, dependencias regenerables y .env; revisa la selección para tu proyecto.
import argparse
import hashlib
from datetime import datetime, timezone
from pathlib import Path
from uuid import uuid4
from zipfile import ZipFile, ZIP_DEFLATED
parser = argparse.ArgumentParser(description="Copia ZIP de una carpeta detenida")
parser.add_argument("source", type=Path)
parser.add_argument("destination", type=Path)
args = parser.parse_args()
source = args.source.resolve(strict=True)
destination = args.destination.resolve()
if not source.is_dir():
parser.error("El origen debe ser una carpeta")
if destination == source or source in destination.parents:
parser.error("El destino debe quedar fuera del origen")
destination.mkdir(parents=True, exist_ok=True)
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
final = destination / f"{source.name}-{stamp}-{uuid4().hex[:8]}.zip"
partial = final.with_suffix(".zip.partial")
excluded = {".git", "node_modules", ".venv", "__pycache__"}
count = 0
def walk_error(error):
raise error
try:
with ZipFile(partial, "x", compression=ZIP_DEFLATED) as archive:
for folder, dirs, files in source.walk(on_error=walk_error):
dirs[:] = [name for name in dirs
if name not in excluded and not (folder / name).is_symlink()]
for name in files:
path = folder / name
if path.is_symlink() or name == ".env" or name.startswith(".env."):
continue
if path.is_file():
archive.write(path, arcname=path.relative_to(source).as_posix())
count += 1
with ZipFile(partial) as archive:
damaged = archive.testzip()
if damaged is not None:
raise RuntimeError(f"CRC incorrecto: {damaged}")
partial.replace(final)
with final.open("rb") as stream:
digest = hashlib.file_digest(stream, "sha256").hexdigest()
final.with_suffix(".zip.sha256").write_text(
f"{digest} {final.name}\n", encoding="utf-8")
print(f"{count} archivos: {final}")
print(f"SHA-256: {digest}")
except Exception:
if partial.exists():
partial.unlink()
raiseLa fecha UTC y el sufijo aleatorio evitan que dos ejecuciones normales elijan el mismo nombre. El modo x exige crear un archivo nuevo. Primero se escribe .partial: hasta que se cierra y pasa testzip, no se renombra a .zip.
Path.walk permite quitar carpetas de dirs antes de descender. No seguimos enlaces simbólicos y tampoco los añadimos como archivos. Esto no sustituye una frontera de seguridad frente a alguien que modifica las rutas durante la ejecución.
on_error hace fallar el programa si no puede recorrer una carpeta: silenciar ese error produciría una copia incompleta. CRC detecta daños dentro del ZIP y SHA-256 permite comparar después el archivo completo. Ninguno demuestra que el origen estuviera en un estado coherente.
Ejecuta y ensaya una restauración
Ejemplo en Windows, con comillas para rutas que contienen espacios:
py backup.py "C:\proyectos\bot" "D:\copias\bot"En Linux o macOS usa python3 backup.py /srv/proyectos/bot /srv/copias/bot. El programa imprime cantidad de archivos, ruta y huella. Si falta un permiso o se llena el disco, termina con error y no deja el temporal como una copia final.
Abre el ZIP y extrae una muestra en una carpeta de ensayo vacía. Verifica que la estructura relativa sea correcta. Si necesitas secretos, recupéralos desde tu gestor o copia protegida: este script los excluye y no cifra el ZIP. Tampoco preserva directorios vacíos ni todos los permisos y metadatos del sistema; no es una imagen completa del disco.
Después puedes programar esa misma orden con el Programador de tareas de Windows o el planificador de tu servidor. Usa rutas absolutas y el intérprete correcto, registra la salida y comprueba el código de retorno. Ejecuta solo una copia de ese origen a la vez. No añadimos eliminación automática: decide la retención después de probar recuperación y disponer de otra copia fuera del equipo.
Un corte de energía entre el renombrado y la escritura de la huella puede dejar un ZIP sin .sha256. Revísalo antes de considerarlo completo. Otra carpeta en el mismo disco no protege frente a una avería del disco; utiliza además un destino independiente para datos irremplazables.
Fuentes y documentación
Proyecto: Python. Desarrollador: comunidad Python y Python Software Foundation. Descarga oficial: python.org. Licencia: PSF License y avisos incorporados. Script, banner y diagrama originales de The Silencers.






Comments 0
Join the conversation with your account.
Sign inLoading comments…