Create a CSV list of files in a folder
Scan subfolders and record file paths, extensions, sizes, and modification times in a table. Start with 4 small example files while keeping the originals and existing results intact.
Compare old and new names in a table and check for conflicts. Only an explicit --apply creates copies in a new folder, keeping the originals.
This translation was generated by AI. Check the code, units, and numbers against the original. Native-speaker review has not yet been completed for each language. 한국어
Who this is forBeginners who want to rename multiple files but are concerned about incorrect rules or overwriting files
In a batch operation, preventing an incorrect rule from being applied to every file matters more than catching a typo in one file. This example validates the entire name list before showing the plan. The default command does not copy files or create folders. After checking the plan, run the operation separately with --apply.
python example.py| old_name | new_name |
|---|---|
| 메모 초안.txt | note-draft.txt |
| 견적 1.txt | quote-001.txt |
| 사진 설명.txt | photo-notes.txt |
Each mapping row links one original to one result. Enter old_name exactly as the actual filename appears, including letter case. The entire operation stops if you omit an original or add a name that does not exist. This rule catches mistakes such as using an old mapping after a new file has been added to the folder.
Values in new_name are treated as conflicting even if they differ only in letter case. This prevents report.txt and REPORT.txt from becoming separate results. Leading or trailing spaces, trailing dots, path separators, and Windows reserved names are also rejected. A person must make the final check in the preview that the naming rules fit the task.
make_plan checks all inputs and conflicts. main prints the validated plan, then checks whether --apply was supplied. Even when applying the plan, it copies file contents under new names instead of using rename on the originals, so you can still inspect the starting state.
"""기본은 미리보기입니다. --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)
The store_true option in argparse makes preview the default when --apply is omitted. Copy destinations are opened in xb mode to avoid overwriting a file that already exists. The completed mapping is also recorded in manifest.csv so you can trace a new name back to its original name.
After checking the three preview lines, run the following command in the same folder. If you edited mapping.csv after the preview, run the default command again to check the new plan. The apply command does not remember the previous preview; it rereads and validates the mapping at execution time.
python example.py --apply| Location | Expected result |
|---|---|
| source_files/ | 3 original files kept under their existing names |
| outputs/renamed/ | note-draft.txt, quote-001.txt, photo-notes.txt |
| outputs/manifest.csv | 3 original → new name mapping rows after the header |
The terminal displays “완료: 3개 Copy본” (complete: three copies) and the output folder path. Open each new file to check its contents as well as its name. For example, the practice document number in note-draft.txt should match the number in 메모 초안.txt.
If validation happened one file at a time inside the copy loop, the first file could be processed before the second caused a failure. This example checks all names first so these pre-check errors do not cause partial application. It also stops before starting if outputs already exists.
Start with a small copy of a single folder when applying this to your work. Put copies of the originals in source_files and write one row per file in mapping.csv. Use the full name, including the extension. An extension does not convert the file format, so do not map a text file to a name ending in .pdf.
If you add numbers, use a fixed number of digits such as 001 and 002, and use a consistent date order to make results easier to find. Agree on which fields belong in the name first, then check their order and any omissions in the preview. This example does not extract titles from document contents or infer your intent to generate names automatically.
| Message | What to check | Solution |
|---|---|---|
| New-name collision (새 이름 충돌) | Identical new_name values or values that differ only in letter case | Choose distinct final names. |
| Original specified more than once | old_name repeated in two rows | Keep one row per original. |
| Must match the complete set of original files | A missing file or a typo in old_name | Compare the folder contents with the mapping again. |
| Invalid filename | Paths, reserved names, forbidden characters, or trailing dots | Enter only the filename and remove restricted characters. |
| outputs already exists | Results from a previous copy operation | Rename the result folder to keep it, then start again. |
Repeating the same command after applying a plan does not update existing results. Even the preview stops if it finds an existing outputs folder. This rule keeps the results of separate operations from being mixed. No overwrite option is provided.
This example only reads the original files and gives the copies new names. Keeping both the originals and their copies requires additional storage. It preserves document contents, but it is not a tool for duplicating creation and modification times, access permissions, or all operating-system metadata.
Errors found before copying, such as name conflicts, stop the entire operation. Once copying starts, a power failure or insufficient disk space is not rolled back as a single transaction. Some files may remain in outputs, so check the completion message and all three files. Concurrent changes to originals or the mapping by another program also require a separate design.
2026-09-19 · Windows 11 · CPython 3.12.14 · No additional packages · Run in a temporary copy of the distribution
Includes code, input data, and instructions. Extract the ZIP and read README.txt first.
Download example ZIPExample code, filenames, and input keys remain unchanged. Refer to the commands and checking steps in the translated article as well.
Practice materials created for this site · Keep your originals separately before running.
The explanations and examples were written for this site. See the official sources below for the related behavior and concepts.