Skip to content

Instantly share code, notes, and snippets.

@fedir
Last active September 7, 2025 16:36
Show Gist options
  • Save fedir/a3c8935016db290771c771c08d87da41 to your computer and use it in GitHub Desktop.
Save fedir/a3c8935016db290771c771c08d87da41 to your computer and use it in GitHub Desktop.
rename_files.py - Rename files by removing accents and special chars, safe for UTF-8 (useful when sending files between different OS, ex.: Mac to Windows)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
rename_files.py - Rename files by removing accents and special chars, safe for UTF-8
Usage:
python3 rename_files.py <directory> [--dry-run]
Features:
- Normalizes accents (NFKD)
- Replaces spaces and special chars with underscores
- Preserves file extensions
- Optional dry-run mode
License:
GNU General Public License v3.0
"""
import os
import sys
import unicodedata
import re
def normalize_filename(name: str) -> str:
"""
Normalize filename: remove accents, replace non-alphanumeric chars with '_'.
"""
# Separate accents
nfkd = unicodedata.normalize('NFKD', name)
# Remove accents
no_accents = ''.join(c for c in nfkd if not unicodedata.combining(c))
# Split name and extension
base, ext = os.path.splitext(no_accents)
# Replace non-word chars with _
base = re.sub(r'[^\w]+', '_', base)
# Remove leading/trailing _
base = base.strip('_')
return f"{base}{ext}"
def main():
if len(sys.argv) < 2:
print("Usage: python3 rename_files.py <directory> [--dry-run]")
sys.exit(1)
directory = sys.argv[1]
dry_run = '--dry-run' in sys.argv
if not os.path.isdir(directory):
print(f"Error: {directory} is not a directory")
sys.exit(1)
for entry in os.listdir(directory):
path = os.path.join(directory, entry)
if os.path.isfile(path):
new_name = normalize_filename(entry)
if new_name != entry:
if dry_run:
print(f"[DRY-RUN] Would rename: {entry} -> {new_name}")
else:
new_path = os.path.join(directory, new_name)
print(f"Renaming: {entry} -> {new_name}")
os.rename(path, new_path)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment