Renombrar muchos archivos con Python o con un agente de IA
Tienes 300 fotos que empiezan con 20240703_ y quieres quitar ese prefijo. Hacerlo una por una es tedioso y es justo el tipo de tarea que un computador hace mejor que tú. Esta es, sin duda, una de las razones por las que me encanta Python: permite automatizar pequeñas tareas y optimizar el tiempo.
En este artículo vemos dos maneras de resolverlo: un script de Python que puedes leer, probar y repetir, y un agente de IA al que le describes lo que quieres. También cuándo conviene cada una y qué riesgos tiene.
Para quién es: para quien nunca ha automatizado una tarea con archivos, aunque sepa muy poco de programación. Qué te llevas: un script corto probado con archivos de ejemplo, un prompt listo para copiar y un criterio para elegir entre ambos caminos.
[!NOTE] Respuesta rápida: guarda el script de abajo como
renombrar.pyy ejecútalo primero sin--aplicar: te muestra qué cambiaría sin tocar nada. Si el plan te convence, repítelo con--aplicar. Si prefieres un agente de IA, trabaja sobre una copia de la carpeta y pídele que muestre el plan antes de ejecutar.
Lo que necesitas antes de empezar
Los comandos se escriben en la terminal: PowerShell en Windows, la app Terminal en macOS y Linux. Necesitas Python 3 instalado. En macOS y Linux el comando suele ser python3; en Windows puedes usar python o py. El script usa solo la biblioteca estándar, así que no hay que instalar nada con pip.
Un script es simplemente un archivo de texto con instrucciones que el computador ejecuta de arriba abajo. Aquí el archivo se llamará renombrar.py.
Por qué el script típico es peligroso
La idea más simple, y la que yo mostraba en la primera versión de este artículo, es: listar los archivos de la carpeta, quedarse con los que terminan en .jpg, quitarles el texto sobrante con .replace() y renombrarlos con os.rename. Funciona en la demo. Con archivos reales tiene tres problemas:
1. Puede sobrescribir archivos sin avisar
Según la documentación de Python, Path.rename() en Unix (Linux y macOS) reemplaza el destino en silencio si ya existe y es un archivo; en Windows, en cambio, lanza FileExistsError. Lo mismo ocurre con os.rename. Es decir, el mismo script se comporta distinto según el sistema, y en uno de ellos puede destruir datos.
Un ejemplo claro: tienes 20240703_a.jpg y también a.jpg. Al quitar el prefijo, ambos quieren llamarse a.jpg. En Linux o macOS el segundo archivo desaparece sin ningún mensaje.
2. No reconoce todas las extensiones
Comparar con endswith(".jpg") ignora FOTO.JPG y foto.jpeg. Las cámaras y teléfonos suelen escribir las extensiones en mayúsculas. El resultado es el peor tipo de falla: el script termina sin errores y sin haber hecho todo el trabajo.
3. No hay vista previa
Si te equivocas en el texto a quitar, el daño ya está hecho cuando lo notas. Lo sensato es mostrar primero el plan y ejecutar solo cuando lo confirmes. En inglés a esto se le llama dry run, un simulacro de la operación.
El script clásico, corregido
Este script usa solo pathlib, la biblioteca de Python que trata las rutas de archivos como objetos y no como simples textos, lo cual es muy cómodo si estás empezando (pathlib). Recibe la carpeta y el texto que quieres quitar, hace un simulacro por defecto, compara extensiones sin distinguir mayúsculas, omite las colisiones y al final imprime un resumen. Guárdalo como renombrar.py:
import argparse
import sys
from collections import Counter
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser(description="Quita un texto del nombre de varios archivos.")
parser.add_argument("carpeta", type=Path, help="carpeta con los archivos")
parser.add_argument("quitar", help="texto que se elimina del nombre, por ejemplo 20240703_")
parser.add_argument("--ext", default=".jpg,.jpeg", help="extensiones a procesar, separadas por comas")
parser.add_argument("--aplicar", action="store_true", help="renombra de verdad (sin esto solo muestra el plan)")
args = parser.parse_args()
if not args.carpeta.is_dir():
sys.exit(f"No es una carpeta: {args.carpeta}")
# ".jpg" o "jpg" valen igual; las entradas vacías se ignoran
extensiones = {"." + e.strip().lower().lstrip(".") for e in args.ext.split(",") if e.strip()}
# 1. Plan: (archivo, nuevo nombre). Solo se cambia el nombre, nunca la extensión
plan = []
for archivo in sorted(args.carpeta.iterdir()):
if archivo.is_file() and archivo.suffix.lower() in extensiones:
nuevo = archivo.stem.replace(args.quitar, "")
if nuevo and nuevo != archivo.stem:
plan.append((archivo, archivo.with_name(nuevo + archivo.suffix)))
# 2. Colisiones: el destino ya existe o dos archivos terminarían con el mismo nombre
repetidos = Counter(destino.name.lower() for _, destino in plan)
contadores = {"renombrados": 0, "omitidos": 0, "fallidos": 0}
for origen, destino in plan:
# samefile: en macOS y Windows "Foto.jpg" y "foto.jpg" son el mismo archivo.
# is_symlink: un enlace simbólico en el destino (incluso roto) cuenta como ocupado.
ocupado = destino.is_symlink() or (destino.exists() and not destino.samefile(origen))
if ocupado or repetidos[destino.name.lower()] > 1:
print(f"OMITIDO {origen.name} -> {destino.name} (colisión)")
contadores["omitidos"] += 1
continue
if args.aplicar:
try:
origen.rename(destino) # ya comprobamos que destino no existe
except OSError as error: # permisos, archivo en uso, cambios en la carpeta...
print(f"FALLÓ {origen.name}: {error}")
contadores["fallidos"] += 1
continue
print(f"{'RENOMBRA' if args.aplicar else 'SIMULA':9} {origen.name} -> {destino.name}")
contadores["renombrados"] += 1
modo = "aplicado" if args.aplicar else "simulacro, no se tocó nada (usa --aplicar)"
print(f"\n{contadores['renombrados']} a renombrar, {contadores['omitidos']} omitidos, "
f"{contadores['fallidos']} fallidos: {modo}")
return 1 if contadores["omitidos"] or contadores["fallidos"] else 0
if __name__ == "__main__":
sys.exit(main())
Dónde y cómo usarlo: guarda el archivo como renombrar.py y abre la terminal en la carpeta donde lo guardaste. Cambia ./fotos por la ruta de tu carpeta (en Windows, por ejemplo, C:\Users\tu-usuario\Pictures\fotos; ponla entre comillas si tiene espacios). En macOS y Linux escribe python3 en lugar de python.
Cómo se usa
Primero el simulacro, que no toca nada:
python renombrar.py ./fotos 20240703_
Si el plan se ve bien, el mismo comando con --aplicar:
python renombrar.py ./fotos 20240703_ --aplicar
Por defecto procesa .jpg y .jpeg. Para otras extensiones usa --ext, por ejemplo --ext .png,.webp.
Dónde y cómo usarlo: ejecuta estos comandos desde la terminal, abierta en la carpeta del script, y cambia 20240703_ por el texto que quieres quitar. La carpeta no se recorre de forma recursiva: solo se procesan los archivos de ese nivel. La extensión se compara sin distinguir mayúsculas, así que .JPG y .jpeg también cuentan.
Lo que hace diferente a esta versión
- Simulacro por defecto. Sin
--aplicarsolo imprimeSIMULA vieja -> nueva. Esto te permite ver el plan completo antes de cambiar nada. - Compara extensiones en minúsculas.
archivo.suffix.lower()cubre.jpg,.JPGy.Jpg. - Omite colisiones. Si el destino ya existe, o si dos archivos terminarían con el mismo nombre, los marca como
OMITIDOy no los toca. Así nunca depende de lo que hagarenameen tu sistema operativo. - Nunca toca la extensión. El texto se quita solo del nombre (
archivo.stem), así que--exty la extensión del archivo quedan intactos. - Ignora lo que no cambia. Si el nombre no contiene el texto a quitar, o si quedaría vacío, el archivo no entra al plan.
- Resumen final y errores controlados. Cuenta los renombrados, omitidos y fallidos. Si el sistema rechaza un cambio (por permisos o porque un archivo está en uso), lo marca como
FALLÓy sigue con el resto en vez de detenerse a mitad. Termina con código 0 si todo salió limpio y 1 si hubo omitidos o fallos, útil si lo usas dentro de otro script.
Cómo se probó
El script se probó con una carpeta de ejemplo (Python 3.13 en Linux) que contenía 20240703_a.jpg, 20240703_b.JPG, 20240703_c.png, 20240703_d.jpeg, a.jpg y notas.txt. Estos son los resultados:
Tabla 1: Resultado del script sobre la carpeta de ejemplo
| Archivo | Resultado |
|---|---|
20240703_a.jpg | Omitido: a.jpg ya existe (colisión) |
20240703_b.JPG | Renombrado a b.JPG (la extensión en mayúsculas cuenta) |
20240703_d.jpeg | Renombrado a d.jpeg |
20240703_c.png | Sin cambios (.png no está en la lista por defecto) |
a.jpg, notas.txt | Sin cambios |
Cómo leerla: lo importante es la primera fila. El archivo en colisión se omite y el contenido de a.jpg sigue intacto; se comprobó el contenido de cada archivo después de aplicar los cambios. El simulacro imprimió exactamente el mismo plan y no modificó ningún archivo.
La otra opción: pedírselo a un agente de IA
Hoy puedes obtener el mismo resultado sin escribir una línea de código: abres un agente de IA de programación en tu carpeta, describes lo que quieres y él escribe y ejecuta los comandos. Herramientas como Claude Code, OpenAI Codex y Google Antigravity funcionan así.
Lo que cambia respecto al script:
- Tú describes el resultado, no el procedimiento. Eso es más rápido de empezar, pero depende de que expliques bien el caso.
- El agente decide cómo hacerlo. Puede escribir un script como el de arriba, usar comandos del sistema o editar los nombres uno por uno. Cada ejecución puede ser distinta.
- El agente ejecuta comandos en tu carpeta. Esa es la diferencia que más importa y la veremos con cuidado en la siguiente sección.
Un prompt para copiar
Un prompt claro tiene contexto, una tarea concreta, ejemplos y restricciones. Las etiquetas ayudan a separar cada parte, como explico en la guía práctica de ingeniería de prompts. Puedes copiar este y cambiar lo que está entre corchetes:
<contexto>
Estoy en la carpeta [./fotos]. Tiene fotos .jpg, .JPG y .jpeg, y otros archivos que no quiero tocar.
</contexto>
<tarea>
Quita el texto "[20240703_]" del nombre de las fotos.
Ejemplo: 20240703_playa.jpg -> playa.jpg
</tarea>
<restricciones>
- Antes de cambiar nada, muéstrame el plan completo (nombre actual -> nombre nuevo) y espera mi confirmación.
- No sobrescribas ningún archivo. Si un nombre nuevo ya existe o dos archivos quedarían con el mismo nombre, omítelos y avísame.
- Procesa las extensiones sin distinguir mayúsculas y no toques otros tipos de archivo.
- Guarda un registro (por ejemplo mapeo.csv) con "nombre viejo,nombre nuevo" para poder deshacer el cambio.
- Prefiere escribir un script de Python con pathlib en lugar de ejecutar comandos sueltos, para que yo pueda revisarlo y repetirlo.
</restricciones>
Dónde y cómo usarlo: abre la terminal (o el editor con el agente) en la carpeta de trabajo, o mejor, en una copia de ella. Inicia el agente (en Claude Code, el comando claude; en Codex, codex; Antigravity es un editor con panel de agente) y pega el prompt. Cambia lo que está entre corchetes. El mismo prompt sirve en las tres herramientas, y funciona igual en Windows, macOS y Linux porque le pides un script de Python en vez de comandos propios de un sistema.
El riesgo real: el agente ejecuta comandos
Un chat solo te responde con texto. Un agente actúa: lee tus archivos, ejecuta comandos y los modifica. Si se equivoca de carpeta, interpreta mal un patrón o un nombre, el daño es real. Estas son las medidas que recomiendo:
- Trabaja sobre una copia de la carpeta, o dentro de un repositorio de Git para poder volver atrás con un commit. Si Git es nuevo para ti, empieza por qué es Git y por qué importa al programar con IA. La documentación de Codex recomienda precisamente un flujo con control de versiones: una rama de trabajo,
git statuslimpio antes de delegar y commits frecuentes. - Pide siempre el plan antes de ejecutar y revísalo. Con 300 archivos, mira una muestra y busca casos raros.
- Usa el modo de aprobación de la herramienta y no lo desactives. Cada una lo llama distinto, así que conviene leer la documentación oficial:
- Claude Code: en el modo por defecto (llamado Manual en la CLI) pide permiso antes de ejecutar comandos de shell, salvo un conjunto de comandos de solo lectura. El modo
acceptEditsacepta automáticamente las ediciones de archivos y comandos comomvocpdentro de la carpeta de trabajo: cómodo, pero precisamente por eso conviene no activarlo hasta confiar en el plan. - OpenAI Codex: combina un sandbox (qué puede hacer técnicamente) con una política de aprobación (cuándo debe preguntarte). Su preajuste
Autopermite leer, editar y ejecutar comandos dentro del directorio de trabajo sin preguntar, y pide aprobación para salir de él o usar la red; al abrirlo recomiendaAutopara carpetas con control de versiones yread-onlypara las que no lo tienen. - Google Antigravity: su ajuste de ejecución de comandos de terminal tiene la opción Request Review, que pide confirmación antes de ejecutar cualquier comando (salvo los de tu lista de permitidos), y Always Proceed, que los ejecuta sin preguntar (salvo los de tu lista de denegados) y la propia documentación describe como de alta autonomía y alto riesgo.
- Claude Code: en el modo por defecto (llamado Manual en la CLI) pide permiso antes de ejecutar comandos de shell, salvo un conjunto de comandos de solo lectura. El modo
- Guarda el mapeo viejo → nuevo. Con ese registro puedes deshacer el cambio aunque no uses Git.
[!WARNING] Estos modos y nombres cambian con las versiones de cada herramienta. Revisa la documentación oficial (enlaces al final) antes de confiar en un ajuste. Y recuerda que un modo de aprobación es una ayuda, no una garantía: el agente puede equivocarse aunque tú apruebes cada paso, por eso la copia o el repositorio siguen siendo tu red de seguridad.
¿Script o agente? Cuándo usar cada uno
Tabla 2: Script de Python frente a agente de IA para renombrar archivos
| Criterio | Script de Python | Agente de IA |
|---|---|---|
| Control | Total: ves cada línea y cada regla | Depende de que revises el plan y los comandos |
| Reproducibilidad | Alta: el mismo script da el mismo resultado | Variable, salvo que le pidas que guarde un script |
| Rapidez para empezar | Media: hay que escribirlo o adaptarlo una vez | Alta: describes el caso y arranca |
| Reglas complejas | Tú las programas | Las interpreta a partir de ejemplos |
| Riesgo | Bajo con simulacro y comprobación de colisiones | Mayor: ejecuta comandos en tu carpeta |
| Costo | Gratis | Requiere una suscripción o uso de API de la herramienta |
Cómo leerla: cada fila compara una cualidad. El script gana en control, repetición y costo; el agente gana en velocidad cuando la regla no es obvia o cuando no quieres programar. No hay un ganador absoluto: depende de si la tarea se repetirá y de cuánto riesgo toleras.
Mi criterio:
- Usa el script si la regla es simple, si vas a repetirla (por ejemplo cada vez que llegan fotos nuevas) o si los archivos son importantes y no tienes una copia.
- Usa el agente para una regla irregular que cuesta expresar en código (“pon la fecha al principio y deja el resto en minúsculas, pero solo en las fotos de la carpeta de eventos”), o para una tarea única que no justifica escribir nada.
- Combina ambos: pídele al agente que escriba el script (como en el prompt de arriba), revísalo tú y guárdalo. Así tienes la rapidez del agente y la repetibilidad del script.
Adaptar el script a otras reglas
El corazón del script es una sola línea: nuevo = archivo.stem.replace(args.quitar, ""). Trabaja sobre el nombre sin la extensión, que se vuelve a pegar después. Cambia esa línea y obtienes otra regla:
- Agregar un prefijo:
nuevo = "vacaciones_" + archivo.stem - Pasar a minúsculas:
nuevo = archivo.stem.lower() - Cambiar espacios por guiones:
nuevo = archivo.stem.replace(" ", "-")
Dónde y cómo usarlo: estas son líneas sueltas que sustituyen a la línea nuevo = ... dentro de renombrar.py, en la sección “Plan”. El resto del script (simulacro, colisiones y resumen) sigue funcionando igual. Si cambias la regla, el argumento quitar deja de usarse, pero sigue siendo obligatorio al ejecutar: pásale cualquier valor (por ejemplo python renombrar.py ./fotos x) o borra su línea add_argument. Ejecuta siempre el simulacro primero. La regla de minúsculas se probó en Linux; en macOS y Windows, donde Foto.jpg y foto.jpg son el mismo archivo, el script lo detecta con samefile y no lo marca como colisión, pero ese caso no se probó en esos sistemas.
En resumen
Path.renamereemplaza en silencio un archivo existente en Linux y macOS y falla en Windows: comprueba siempre que el destino no exista.- Compara extensiones con
suffix.lower()y muestra un simulacro antes de aplicar. - Un agente de IA es útil para reglas irregulares o tareas únicas, pero ejecuta comandos en tu carpeta: usa una copia o Git, pide el plan y no desactives la aprobación de comandos.
- Lo mejor de ambos mundos: que el agente escriba un script, lo revises y lo guardes.
Tu primer paso
Crea una carpeta de prueba con cinco archivos falsos (touch a.jpg b.JPG en macOS y Linux, o créalos a mano en Windows), guarda el script y ejecuta el simulacro. Cuando entiendas cada línea del plan que imprime, pruébalo con tus archivos reales, siempre sobre una copia.
Si en tu empresa hay tareas repetitivas con archivos (renombrar, convertir, ordenar, extraer datos) y quieres automatizarlas, escríbeme a contact@camilordz.com.
Fuentes y lecturas recomendadas
- Python: documentación de pathlib (
Path.rename,Path.suffix,Path.exists) y argparse. - Claude Code: configurar permisos.
- OpenAI Codex: aprobaciones y seguridad del agente.
- Google Antigravity: documentación de ajustes.
- Artículos relacionados en este blog:
El siguiente paso: cómo llevar esto a la práctica
Guías de laboratorio y arquitecturas técnicas para resolver este problema sin rodeos.

Segunda parte de la guía de Git para vibe coders: qué son las ramas y cómo usarlas para cada experimento con IA, cómo resolver un conflicto de merge y cuándo usar git restore, reset, revert o reflog para deshacer errores sin perder trabajo.

Aprende a diseñar la arquitectura de tu sitio web: silos temáticos, profundidad de clics vs URLs, navegación facetada sin problemas de indexación y migas de pan con Schema.

Seguridad en WordPress con datos reales: analicé 1.000 ataques bloqueados. Casi nadie va por la contraseña; más del 40 % busca archivos sensibles (.env, .git).
