Scripts y automatización de archivos

Crear una vista previa antes de renombrar archivos en lote

Revisa en una tabla los nombres actuales y los nuevos, y comprueba posibles conflictos. Solo al indicar --apply se crean copias en una carpeta nueva, conservando los originales.

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. 한국어

Para quién esPrincipiantes que quieren renombrar varios archivos, pero les preocupan las reglas incorrectas y las sobrescrituras

Preparación
  • Prepara Python 3.12 o posterior y comprueba la versión con python --version en la terminal.
  • Extrae el ZIP de ejemplo en una carpeta nueva. No lo ejecutes directamente dentro del archivo comprimido.
  • Abre la terminal en la carpeta donde está example.py. Si el comando python no está disponible en Windows, usa py; en macOS y Linux, usa python3 según tu entorno.
  • No necesitas instalar paquetes externos ni tener una cuenta. Los materiales incluidos son datos sintéticos de elaboración propia.

01Empezar con una vista previa de tres nombres

Al renombrar en lote, lo primero es evitar que una regla incorrecta se aplique a todos los archivos, más que un error de escritura en uno solo. Este ejemplo valida toda la lista de nombres antes de mostrar el plan. El comando predeterminado no copia archivos ni crea carpetas. La operación real se ejecuta por separado, añadiendo --apply después de revisar el plan.

  1. Comprueba mapping.csv y los tres archivos originales de source_files en la carpeta extraída.
  2. Compara old_name y new_name en mapping.csv. A la izquierda está el nombre actual; a la derecha, el de la nueva copia.
  3. Ejecuta el siguiente comando predeterminado y comprueba que las tres parejas de nombres unidas por flechas sean las previstas.
  4. Comprueba que en esta fase source_files no haya cambiado y que outputs aún no exista.
bash
python example.py

02Cómo leer la tabla de correspondencias

old_namenew_name
메모 초안.txtnote-draft.txt
견적 1.txtquote-001.txt
사진 설명.txtphoto-notes.txt

Cada fila de la tabla relaciona un original con un resultado. Escribe old_name exactamente como el nombre real, incluidas las mayúsculas y minúsculas. Si omites un original o añades un nombre inexistente, se detiene toda la operación. Esta regla permite detectar el error de usar una tabla antigua después de añadir archivos nuevos a la carpeta.

Los valores de new_name se consideran en conflicto aunque solo difieran en mayúsculas y minúsculas. Esta decisión evita crear report.txt y REPORT.txt como resultados distintos. También se rechazan espacios al principio o al final, puntos finales, separadores de ruta y nombres reservados de Windows. Una persona debe confirmar en la vista previa que las reglas de nombres sean adecuadas para el trabajo.

03Código completo para ejecutar

make_plan comprueba todas las entradas y los conflictos. main muestra el plan validado y después comprueba si se ha indicado --apply. Incluso al aplicar el plan, no se usa rename para cambiar el nombre del original: se copia el contenido con un nombre nuevo, de modo que puedas volver a comprobar el estado inicial.

example.py
"""기본은 미리보기입니다. --apply일 때만 새 이름의 복사본을 만듭니다."""

import argparse
import csv
import re
import shutil
import sys
from pathlib import Path

BASE = Path(__file__).resolve().parent
INPUT = BASE / "source_files"
MAPPING = BASE / "mapping.csv"
OUTPUT = BASE / "outputs"
RESERVED = {"CON", "PRN", "AUX", "NUL"} | {
    f"{prefix}{number}" for prefix in ("COM", "LPT") for number in range(1, 10)
}


def validate_name(name):
    # 폴더 경로, Windows 예약 이름, 제어 문자 등을 이름으로 받지 않습니다.
    if (not name or name in {".", ".."} or name != name.strip()
            or name.endswith(".") or re.search(r'[<>:"/\\|?*\x00-\x1f]', name)
            or name.split(".")[0].upper() in RESERVED):
        raise ValueError(f"사용할 수 없는 파일명: {name!r}")


def make_plan():
    if (INPUT.is_symlink() or not INPUT.is_dir()
            or getattr(INPUT, "is_junction", lambda: False)()):
        raise ValueError("source_files는 실제 폴더여야 합니다.")
    if MAPPING.is_symlink():
        raise ValueError("mapping.csv 링크는 처리하지 않습니다.")
    sources = list(INPUT.iterdir())
    if any(path.is_symlink() or not path.is_file() for path in sources):
        raise ValueError("source_files에는 일반 파일만 넣으세요.")
    if not sources:
        raise ValueError("source_files에 파일이 없습니다.")
    plan, old_keys, new_keys = [], set(), set()
    with MAPPING.open("r", encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream, strict=True)
        if reader.fieldnames != ["old_name", "new_name"]:
            raise ValueError("mapping.csv의 열은 old_name,new_name이어야 합니다.")
        for row in reader:
            if None in row or any(value is None for value in row.values()):
                raise ValueError(f"mapping.csv {reader.line_num}행의 열 수를 확인하세요.")
            old, new = row["old_name"], row["new_name"]
            validate_name(old)
            validate_name(new)
            if old.casefold() in old_keys:
                raise ValueError(f"원본이 중복 지정되었습니다: {old}")
            if new.casefold() in new_keys:
                raise ValueError(f"새 이름 충돌: {new}")
            old_keys.add(old.casefold())
            new_keys.add(new.casefold())
            plan.append((old, new))
    # 한 파일이라도 누락되거나 없는 파일을 지정하면 아무것도 복사하지 않습니다.
    if {old for old, _ in plan} != {path.name for path in sources}:
        raise ValueError("mapping.csv의 old_name은 원본 파일 전체와 정확히 일치해야 합니다.")
    if OUTPUT.exists() or OUTPUT.is_symlink():
        raise FileExistsError("outputs가 이미 있습니다. 기존 결과를 옮긴 뒤 실행하세요.")
    return plan


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--apply", action="store_true", help="새 이름의 복사본을 outputs/renamed에 만듭니다.")
    args = parser.parse_args()
    plan = make_plan()  # 충돌 검사는 출력 폴더를 만들기 전에 전체에 대해 끝냅니다.
    for old, new in plan:
        print(f"{old} → {new}")
    if not args.apply:
        print(f"미리보기: {len(plan)}개. 파일은 변경되지 않았습니다.")
        print("이름을 확인한 뒤 python example.py --apply 를 실행하세요.")
        return 0

    OUTPUT.mkdir()
    renamed = OUTPUT / "renamed"
    renamed.mkdir()
    for old, new in plan:
        # 이름 변경 대신 복사합니다. xb는 대상이 생겼으면 덮어쓰지 않고 실패합니다.
        with (INPUT / old).open("rb") as source, (renamed / new).open("xb") as target:
            shutil.copyfileobj(source, target)
    with (OUTPUT / "manifest.csv").open("x", encoding="utf-8-sig", newline="") as stream:
        writer = csv.writer(stream)
        writer.writerow(["old_name", "new_name"])
        writer.writerows(plan)
    print(f"완료: {len(plan)}개 복사본 → {renamed}")
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except (OSError, ValueError, csv.Error) as error:
        print(f"중지: {error}", file=sys.stderr)
        raise SystemExit(2)

La opción store_true de argparse hace que omitir --apply active la vista previa predeterminada. El destino de la copia se abre en modo xb para no sobrescribir un archivo que ya exista. Las correspondencias completadas también se registran en manifest.csv, lo que permite encontrar el nombre original a partir del nuevo.

04Crear copias con el plan revisado

Tras revisar las tres líneas de la vista previa, ejecuta el siguiente comando en la misma carpeta. Si has modificado mapping.csv después de la vista previa, vuelve a ejecutar el comando predeterminado y revisa el nuevo plan. El comando de aplicación no recuerda la vista previa anterior: vuelve a leer y comprobar la tabla que existe en el momento de ejecutarlo.

bash
python example.py --apply
UbicaciónResultado esperado
source_files/Se conservan los 3 originales con sus nombres anteriores
outputs/renamed/note-draft.txt, quote-001.txt, photo-notes.txt
outputs/manifest.csvTras la cabecera, 3 filas de correspondencia entre nombre original→nombre nuevo

La terminal muestra «완료: 3개 Copiar본» y la ruta de la carpeta de salida. Abre cada archivo nuevo para revisar tanto el nombre como el contenido. Por ejemplo, el número del documento de práctica en note-draft.txt debe coincidir con el de 메모 초안.txt.

05Crear un conflicto para comprobar la detención

  1. Extrae el ZIP en otra carpeta nueva. No mezcles las pruebas de errores con una carpeta que ya contiene resultados.
  2. Cambia el segundo new_name de mapping.csv a note-draft.txt, igual que el primero, y guarda el archivo.
  3. Ejecuta python example.py --apply. Debe aparecer el mensaje «새 이름 충돌», que indica un conflicto de nombres nuevos.
  4. Comprueba que no se haya creado outputs y que sigan presentes los tres originales en source_files.
  5. Restaura los valores originales de la tabla y vuelve a empezar por la vista previa.

Si se validara archivo por archivo dentro del bucle de copia, el primero podría procesarse y el segundo fallar. Este ejemplo comprueba todos los nombres por adelantado para que los errores de validación previa no dejen cambios aplicados a medias. También se detiene antes de empezar si outputs ya existe.

06Adaptarlo a las reglas de nombres de tus documentos

Para aplicarlo al trabajo, empieza con una copia pequeña de una carpeta. Coloca copias de los originales en source_files y crea en mapping.csv una fila por archivo. Debes escribir el nombre completo, incluida la extensión. Cambiar la extensión no convierte el formato, así que no asignes .pdf a un archivo cuyo contenido sigue siendo texto.

Si numeras los archivos, fija la cantidad de dígitos, como 001, 002, y usa siempre el mismo orden para las fechas. Así será más fácil encontrar los resultados. Acuerda primero qué elementos incluir en los nombres y revisa el orden y las omisiones en la vista previa. Este ejemplo no extrae títulos del contenido ni genera nombres automáticamente adivinando la intención del usuario.

07Qué revisar según el mensaje de detención

MensajeQué comprobarSolución
새 이름 충돌 (conflicto de nombres nuevos)Valores de new_name iguales o que solo difieren en mayúsculas y minúsculasAsigna nombres finales que se distingan entre sí.
원본이 중복 지정됨 (original indicado más de una vez)old_name se repite en dos filasDeja una sola fila por original.
원본 파일 전체와 일치해야 함 (debe coincidir con todos los archivos originales)Archivo omitido o error de escritura en old_nameVuelve a comparar la lista de la carpeta con la tabla.
사용할 수 없는 파일명 (nombre de archivo no permitido)Rutas, nombres reservados, caracteres prohibidos o punto finalEscribe solo el nombre de archivo y elimina los caracteres no permitidos.
outputs ya existeResultados de una copia anteriorGuarda los resultados con otro nombre y vuelve a empezar.

Repetir el mismo comando después de aplicarlo no actualiza los resultados existentes. La vista previa también se detiene si encuentra outputs. Esta regla evita mezclar resultados de tareas diferentes; no se ofrece una opción de sobrescritura.

08Alcance de la copia y comprobaciones pendientes

Este ejemplo solo lee los archivos originales y cambia los nombres de las copias. Necesitas espacio para conservar los originales y almacenar también las copias. Se conserva el contenido de los documentos, pero no es una herramienta para duplicar de forma idéntica las fechas de creación y modificación, los permisos ni todos los metadatos adicionales del sistema operativo.

Los errores detectados antes de copiar, como los conflictos, detienen toda la operación. Sin embargo, no se revierte todo como una única transacción si se corta la corriente o se agota el espacio después de empezar a copiar. En ese caso, pueden quedar algunos archivos en outputs; comprueba el mensaje de finalización y los tres archivos. Los cambios simultáneos de los originales o de la tabla por otro programa también requieren un diseño específico.

Registro de ejecución y verificación

2026-09-19 · Windows 11 · CPython 3.12.14 · Sin paquetes adicionales · Ejecutado en una copia temporal de la distribución

  • La vista previa predeterminada no crea la carpeta de salida
  • Los bytes de las 3 copias creadas con --apply coinciden con los originales
  • Rechazo de nombres iguales y conflictos de mayúsculas y minúsculas antes de crear la salida
  • Rechazo de originales repetidos, omitidos o inexistentes, rutas que salen de la carpeta permitida y nombres reservados
  • Una nueva ejecución se detiene y conserva los resultados existentes
Límites de la verificación
  • No cambia el nombre de los archivos originales.
  • No revierte automáticamente las salidas parciales tras un error de disco.
  • No conserva todos los metadatos de los archivos.
  • La verificación de ejecución se realizó en Windows.

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

Archivos de ejemplo para ejecutar

Incluye código, datos de entrada e instrucciones de ejecución. Extrae el ZIP y lee primero README.txt.

Descargar ZIP de ejemplo

El código de ejemplo, los nombres de archivo y las claves de entrada se mantienen como en el original. Consulta también los comandos y los pasos de comprobación del texto traducido.

Material de práctica original · Guarda los archivos originales por separado antes de ejecutarlo.

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.