Last active
September 7, 2025 16:36
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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