Cómo copiar y mover archivos con shutil en Python

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Copiando e movendo arquivos com Python usando shutil

El módulo shutil reúne operaciones de alto nivel para copiar, mover y eliminar archivos y carpetas. Es más cómodo que trabajar únicamente con funciones básicas del sistema, pero también puede causar pérdidas si se usa sin validaciones. En esta guía construirás operaciones seguras, con rutas modernas, control de conflictos y registro de resultados.

Para complementar el tema, consulta las guías sobre archivos en Python, módulo os, excepciones, logging y organización automática. Como fuentes externas, revisa la documentación oficial de shutil y la documentación oficial de pathlib.

copy() y copy2()

copy() copia el contenido y los permisos básicos. copy2() intenta conservar metadatos adicionales, como tiempos de modificación. Ninguna función garantiza preservar todos los atributos de todos los sistemas.

from pathlib import Path
from shutil import copy2

origen = Path("informe.pdf")
destino = Path("copias") / origen.name
destino.parent.mkdir(parents=True, exist_ok=True)

copy2(origen, destino)

Usa copy2() para respaldos comunes y copy() cuando los metadatos no sean importantes.

Copiar a otra carpeta con un nombre nuevo

destino = Path("copias") / "informe-final.pdf"
copy2(origen, destino)

El segundo argumento puede ser una carpeta o una ruta completa. Para evitar ambigüedad, construye siempre la ruta final explícitamente.

Evitar sobrescrituras

Las funciones de copia pueden reemplazar un archivo existente. Valida antes y decide una política: detener, generar otro nombre o solicitar confirmación.

if destino.exists():
    raise FileExistsError(f"Ya existe: {destino}")

En procesos automáticos, generar un nombre único suele ser más seguro que sobrescribir.

Mover archivos

from shutil import move

resultado = move(origen, destino)
print(resultado)

Dentro del mismo sistema de archivos, mover puede equivaler a renombrar y ser rápido. Entre discos distintos, puede implicar copia y eliminación. Por eso debes considerar espacio disponible y fallos intermedios.

Renombrar con pathlib

Si solo deseas cambiar el nombre dentro del mismo sistema, Path.replace() o Path.rename() son opciones claras. replace() puede sustituir un destino existente, mientras que el comportamiento de rename() depende del sistema. Verifica siempre el destino.

Copiar carpetas completas

from shutil import copytree, ignore_patterns

copytree(
    "proyecto",
    "respaldo/proyecto",
    ignore=ignore_patterns("*.pyc", "__pycache__", ".git"),
)

copytree() recrea la estructura. En versiones modernas puede combinarse con dirs_exist_ok=True, pero mezclar contenidos existentes requiere una política clara para conflictos.

Ignorar patrones

from shutil import ignore_patterns

ignorar = ignore_patterns(
    ".git",
    ".venv",
    "__pycache__",
    "*.log",
    "*.tmp",
)

Para un respaldo de proyecto, normalmente no conviene copiar entornos virtuales, cachés ni secretos. Revisa también archivos .env.

Copiar solo algunos tipos

Cuando la selección depende de reglas más complejas, recorre con rglob() y copia cada archivo conservando su ruta relativa.

origen = Path("datos")
destino = Path("respaldo")
permitidas = {".csv", ".json", ".txt"}

for ruta in origen.rglob("*"):
    if not ruta.is_file() or ruta.suffix.lower() not in permitidas:
        continue

    relativo = ruta.relative_to(origen)
    final = destino / relativo
    final.parent.mkdir(parents=True, exist_ok=True)
    copy2(ruta, final)

Eliminar carpetas con rmtree()

shutil.rmtree() elimina una carpeta completa y no debe usarse sin controles fuertes.

from shutil import rmtree

objetivo = Path("temporal").resolve()
base_permitida = Path.cwd().resolve()

if base_permitida not in objetivo.parents:
    raise ValueError("Ruta fuera del área permitida")

rmtree(objetivo)

Nunca construyas el objetivo directamente desde una entrada de usuario sin normalizar y validar.

Espacio disponible

from shutil import disk_usage

total, usado, libre = disk_usage(destino.parent)
print(f"Libre: {libre / 1024**3:.2f} GB")

Antes de copiar un árbol grande, compara el tamaño estimado con el espacio libre y conserva un margen.

Verificar copias

Comparar únicamente el tamaño detecta algunos fallos, pero no todos. Para archivos importantes, calcula un hash antes y después.

import hashlib

def sha256(ruta):
    resumen = hashlib.sha256()
    with open(ruta, "rb") as archivo:
        for bloque in iter(lambda: archivo.read(1024 * 1024), b""):
            resumen.update(bloque)
    return resumen.hexdigest()

Los hashes añaden tiempo de lectura, así que decide cuándo la integridad justifica el coste.

Copia atómica

Para no dejar un destino incompleto, copia primero a un nombre temporal en la misma carpeta, verifica y luego reemplaza.

temporal = destino.with_suffix(destino.suffix + ".tmp")
copy2(origen, temporal)

if sha256(origen) != sha256(temporal):
    temporal.unlink()
    raise ValueError("La verificación falló")

temporal.replace(destino)

Registrar operaciones

Guarda origen, destino, tamaño, fecha y resultado. Un log facilita auditoría, pero para deshacer movimientos es mejor un historial estructurado en JSONL o una base de datos pequeña.

Manejo de errores

try:
    copy2(origen, destino)
except FileNotFoundError:
    logging.exception("No existe el origen")
except PermissionError:
    logging.exception("Permiso denegado")
except OSError:
    logging.exception("Falló la operación de archivos")

Procesa cada elemento de forma independiente y muestra un resumen final. No ocultes errores con un except Exception: pass.

Enlaces simbólicos

Decide si deseas copiar el enlace o seguirlo. Seguir enlaces puede sacar el proceso del árbol esperado o crear ciclos. Para datos externos, trata los enlaces como contenido potencialmente peligroso y valida la ruta resuelta.

Pruebas

Usa tempfile.TemporaryDirectory() para crear escenarios controlados. Prueba destinos existentes, permisos, nombres Unicode, archivos vacíos, carpetas anidadas y fallos simulados. Comprueba que un error no borre el origen ni deje temporales abandonados.

Conclusión

shutil simplifica operaciones complejas, pero la seguridad depende de tus reglas. Construye rutas con pathlib, evita sobrescrituras silenciosas, valida áreas permitidas, verifica copias críticas y registra cada acción. Para eliminaciones, añade todavía más controles y utiliza primero un modo de simulación.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Compactando arquivos ZIP automaticamente com Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear archivos ZIP con Python y zipfile

    Crea archivos ZIP con Python y zipfile: carpetas, filtros, arcname, compresión, contenido en memoria, verificación, hashes y backups seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Como evitar KeyError usando defaultdict em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    defaultdict en Python: evita KeyError y simplifica diccionarios

    Aprende defaultdict en Python para evitar KeyError, contar, agrupar, crear estructuras anidadas y compararlo con get, setdefault y Counter.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Redimensionamento de imagens com Pillow em Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Cómo redimensionar imágenes con Pillow en Python

    Redimensiona imágenes con Pillow en Python conservando proporciones, creando miniaturas, corrigiendo EXIF, procesando carpetas y exportando WebP.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Copiando e movendo arquivos com Python usando shutil
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Cómo copiar y mover archivos en Python con shutil

    Aprende shutil en Python para copiar, mover, eliminar y comprimir archivos y carpetas con validaciones, hashes, logs y modo simulación.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Monitoramento de pastas em tempo real com Python Watchdog
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Watchdog en Python: monitoriza carpetas en tiempo real

    Aprende Watchdog en Python para monitorizar carpetas, filtrar eventos, evitar duplicados, esperar archivos completos y automatizar procesos seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Instalação offline de pacotes Python sem internet
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Cómo instalar paquetes de Python sin conexión a Internet

    Aprende a instalar paquetes de Python sin Internet con pip download, ruedas, requirements.txt, --no-index, hashes y repositorios internos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026