How to Safely Bulk Rename Files with Python
Python can rename many files consistently, but the safe part is the planning and rollback—not the rename() call itself. The script below builds a complete plan, detects duplicate and pre-existing target names before touching a file, writes a CSV manifest, requires an exact confirmation, and uses temporary names so source and destination names can overlap.
Decide the naming rule before writing code
Write down the exact source folder, sort order, prefix, starting number, number width, and extension behavior. A script cannot know which photograph should receive a business-specific identifier; it can only apply the rule you define.
Save the script outside the target folder
Create safe_renamer.py somewhere outside the folder being renamed. Replace the example path and prefix:
from pathlib import Path
from uuid import uuid4
import csv
folder = Path(r"C:\Users\YourName\Desktop\ProductPhotos")
prefix = "SUMMER_COLLECTION"
manifest_path = folder / "rename-manifest.csv"
if not folder.is_dir():
raise SystemExit(f"Folder not found: {folder}")
files = sorted(
(
item for item in folder.iterdir()
if item.is_file()
and item.name != manifest_path.name
and not item.name.startswith(".__rename_stage_")
),
key=lambda item: item.name.casefold()
)
if not files:
raise SystemExit("No files were found in the selected folder.")
plan = []
for number, old_path in enumerate(files, start=1):
new_name = f"{prefix}_{number:03d}{old_path.suffix}"
plan.append((old_path, folder / new_name))
# Case-insensitive checks also catch common Windows collisions.
target_keys = [str(target).casefold() for _, target in plan]
if len(target_keys) != len(set(target_keys)):
raise SystemExit("Two source files would receive the same target name.")
source_keys = {str(source.resolve()).casefold() for source, _ in plan}
conflicts = []
for _, target in plan:
if target.exists() and str(target.resolve()).casefold() not in source_keys:
conflicts.append(target.name)
if conflicts:
raise SystemExit(
"Unrelated target files already exist: " + ", ".join(conflicts)
)
if manifest_path.exists():
raise SystemExit(
f"Manifest already exists: {manifest_path}. Move or review it first."
)
with manifest_path.open("w", newline="", encoding="utf-8-sig") as handle:
writer = csv.writer(handle)
writer.writerow(["OriginalName", "NewName"])
writer.writerows((source.name, target.name) for source, target in plan)
print("Planned changes:")
for source, target in plan:
print(f"{source.name} -> {target.name}")
answer = input("Type RENAME to apply this exact plan: ")
if answer != "RENAME":
raise SystemExit(f"No files changed. Review: {manifest_path}")
staged = []
completed = []
try:
for source, target in plan:
temp = folder / f".__rename_stage_{uuid4().hex}{source.suffix}"
source.rename(temp)
staged.append((source, temp, target))
for source, temp, target in staged:
temp.rename(target)
completed.append((source, target))
except Exception:
# Best-effort rollback. Keep the manifest for manual recovery.
for source, target in reversed(completed):
if target.exists() and not source.exists():
target.rename(source)
for source, temp, _ in reversed(staged):
if temp.exists() and not source.exists():
temp.rename(source)
raise
print(f"Renamed {len(completed)} files.")
print(f"Manifest: {manifest_path}")
Preview what the script will include
The script processes files directly inside the selected folder and ignores subfolders. It also excludes its own manifest and temporary staging names. Hidden files are still files, so inspect the printed plan and remove any item that should not be renamed.
Understand extension handling
old_path.suffix preserves only the final suffix. For example, archive.tar.gz is treated as having the suffix .gz. If compound extensions matter, define a separate rule instead of assuming every dot belongs to the extension.
Why the preflight checks matter
- Duplicate target detection: stops two source files from receiving the same name.
- Existing-file detection: stops before an unrelated file would be replaced or block the operation.
- Case-insensitive comparison: catches names that Windows commonly treats as the same.
- CSV manifest: preserves the intended original-to-new mapping before the first rename.
- Two-stage rename: allows names to overlap, such as swapping an existing numbered sequence.
Run a controlled test
- Create a temporary folder containing copies of several real file types.
- Include names that already resemble the target pattern.
- Run the script and stop at the preview the first time.
- Open the CSV manifest and confirm the sort order.
- Run again, type
RENAME, and open several renamed files. - Confirm that the expected count, extensions, and file contents are unchanged.
Recover from an interrupted run
If the script raises an exception, it attempts a best-effort rollback. A computer crash can still leave staging names beginning with .__rename_stage_. Do not delete them. Use the manifest and a backup to restore the mapping. If the folder is synchronized by OneDrive, Google Drive, or another service, pause and review sync activity before retrying.
When not to use sequential renaming
Do not use this approach when filenames contain required case numbers, legal identifiers, dates, or references used by another application unless you have verified every dependency. A database, digital-asset manager, or application-specific export may be the correct place to change those names.
Completion checklist
- The number of renamed files matches the plan.
- No staging names remain.
- Several files open correctly.
- Compound extensions and leading zeros are correct.
- The manifest and original backup are retained until downstream workflows are checked.
Official Python reference
Related Guides
- How to Find Duplicate Files with PowerShell and Export a Report — Review duplicate files before reorganizing a large folder.
- How to Combine Files from a Folder with Excel Power Query — Turn consistently named source files into a refreshable dataset.
About the author
Tweaknook Editorial publishes practical guides and browser-based tools for everyday digital work. Product-dependent facts are checked against current primary documentation, with limitations and safer verification steps stated where relevant.