A command-line tool that renames files and directories to clean, ASCII-safe slugs.
- Transliterates accented and extended Latin characters to ASCII (
é → e,ç → c,œ → oe,ß → ss…) - Expands typographic ligatures and compatibility forms (
fi → fi, fullwidthFile → file, superscripts² → 2,™ → tm,№ → no) - Romanises Greek and Cyrillic letters (
Ελληνικά → ellinika,Москва → moskva) - Lowercases everything
- Replaces spaces and special characters with
-; collapses consecutive separators - Preserves
_; cleans up_-and-_sequences to_ - Strips leading and trailing
-/_before the extension - Preserves known compound extensions (
.tar.gz,.tar.bz2,.tar.xz,.tar.zst) - Keeps extensions separate only when they are ASCII alphanumeric and ≤10 characters
(
.tétis absorbed as-tet,.abcdefghijklas-abcdefghijkl) - Skips hidden files (
.gitignore,.DS_Store…) and reports naming conflicts - Reports names that are not valid UTF-8 as errors and leaves them unchanged
- Optionally (
-D) deletes a source file that is a byte-for-byte duplicate of an existing target - Optional cleanup fixes: repairs mojibake (
-U), strips HTML tags and entities (-H), or both (-A)
Requires Rust 1.85+.
# From crates.io
cargo install rename-simple
# Or from source
cargo install --path .The repository is a flake, so no Rust toolchain is needed:
# Run without installing
nix run github:darkone-linux/rename-simple
# Or install into your profile
nix profile install github:darkone-linux/rename-simplerename-simple [OPTIONS] [files]...
Each argument is renamed itself, like with rename(1). Globbing is left to the
shell: rename-simple *.jpg, or rename-simple dir/**/*.pdf with zsh or bash
globstar. Files and directories are both renamed by default; -f / -d
restrict renaming to one type. Without arguments, rename-simple prints its
help.
| Option | Description |
|---|---|
[files]... |
Entries to rename (files and/or directories) |
-f, --files-only |
Rename files only |
-d, --dirs-only |
Rename directories only |
-U, --fix-unicode |
Repair mojibake (UTF-8 misread as Latin-1/CP1252) before renaming |
-H, --fix-html |
Strip HTML tags and decode HTML entities before renaming |
-A, --fix-all |
Apply every cleanup fix: -U and -H |
-D, --delete-duplicates |
Delete a source that is a byte-for-byte duplicate of an existing target |
-n, --dry-run |
Show what would be done without renaming or deleting anything |
-q, --quiet |
Print nothing, not even errors |
-v, --verbose |
Also list unchanged entries ([X]) |
-h, --help |
Print help |
-V, --version |
Print version |
Renamed entries are printed as [R] lines on standard output, deleted
duplicates ([W]) and errors ([E]) on standard error, followed by a summary
line. The exit status is 0 when the command line is valid, even if some
entries fail, and 2 when it is invalid (unknown option, -f with -d…).
$ cd ~/Downloads
$ rename-simple -n *[R] .: 01_ Introduction au Projet.PDF -> 01_introduction-au-projet.pdf
[R] .: à faire .tét -> a-faire-tet
[R] .: backup.TAR.GZ -> backup.tar.gz
[R] .: Café Montréal.jpg -> cafe-montreal.jpg
[R] .: notes.cuicuicuicui -> notes-cuicuicuicui
[R] .: Réunion d'équipe (2024).docx -> reunion-d-equipe-2024.docx
6 entries matched, 6 entries renamed, 0 error.
Run the same command without -n to apply the renames.
$ rename-simple -f * # files only, directories are ignored
$ rename-simple -d * # directories only, files are ignoredWith zsh, or bash with shopt -s globstar:
$ rename-simple ~/Documents/**/*.pdfNames from broken downloads or web pages may contain mojibake (UTF-8 read as Latin-1/CP1252) or HTML markup. The cleanup fixes repair the name before it is converted:
$ rename-simple -A *[R] .: <b>Été 2024.pdf -> ete-2024.pdf
[R] .: Café Montréal.jpg -> cafe-montreal.jpg
[R] .: Tom & Jerry.mp4 -> tom-jerry.mp4
3 entries matched, 3 entries renamed, 0 error.
-U only changes a name that decodes entirely, so a correct name (café.jpg)
is never altered; double mojibake (Café) is repaired too. -H decodes
named (é), decimal (é) and hexadecimal (é) entities.
Block tags (<br>, <p>, <div>, <li>, <h1> to <h6>…) become a space
(data<br>client → data-client); inline tags (<b>, <i>, <span>…) are
removed (client<b>s → clients). A decoded slash is converted like any other
character (Report / Final.txt → report-final.txt).
An existing entry is never overwritten. When the new name is taken, the entry is left unchanged and the error says whether both files hold the same bytes:
$ rename-simple *[E] .: IMG 9572.mov -> File name already exists (identical duplicate, use -D to delete the source)
[E] .: VID 20260819.mp4 -> File name already exists (2 different files)
4 entries matched, 0 entry renamed, 2 errors.
With -D, a source that is a distinct regular file with identical content,
compared byte for byte, is deleted:
$ rename-simple -D *[W] .: IMG 9572.mov -> Identical duplicate, source deleted
[E] .: VID 20260819.mp4 -> File name already exists (2 different files)
4 entries matched, 0 entry renamed, 1 duplicate removed, 1 error.
-A does not imply -D, and -D with -n only announces the deletion. Every
other conflict leaves both entries unchanged: different content, a directory,
a symlink on either side, or two names for the same entry (hard link,
case-insensitive filesystem). Arguments that would get the same new name are
all left unchanged.
-v also lists the entries left unchanged:
$ rename-simple -v *[X] .: notes.txt
[R] .: Mon Fichier.txt -> mon-fichier.txt
[R] .: Москва -> moskva
3 entries matched, 2 entries renamed, 0 error.
To rename everything in the current directory, run rename-simple *. An alias
in ~/.bashrc or ~/.zshrc makes it shorter:
alias rsa='rename-simple *'cargo testMIT
