rnr renames files by regex, with a dump file you can undo
A command-line tool to batch rename files and directories
At a glance
- What is it?
- rnr is a Rust command line tool for batch renaming files and directories with regular expressions, which runs as a dry run by default, checks for collisions before touching anything, writes every operation to a dump file and can undo them from that file. It suits a person cleaning up a large tree of badly named files, and its defaults are cautious on purpose.
- Who is it for?
- Adopt rnr when you have a directory tree of files whose names follow a pattern you want to rewrite, and you want the change previewed, checked for collisions and reversible. Do not adopt it if you need renaming across remote hosts or a scripted API, since it is a local command line tool with a single set of subcommands.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 18 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Dry run first, force second
The defaults section of the README is the clearest statement of what rnr considers safe, and it reads like a list of deliberate restrictions. It checks all operations to avoid overwriting existing files. It is a dry run by default, so nothing happens until you ask for it. It only accepts UTF-8 valid input arguments and filenames. It works on files and symlinks and ignores directories. It accepts multiple files as arguments and a regex to generate matches, with the same limitations as the regex crate, and it supports numbered and named capture groups. If a max depth is not provided in recursive mode it is assumed infinite. It does not generate backups by default. Its output is colored on ANSI terminals only. It ignores hidden files and directories. It sets the number of replacements to one. And it dumps all operations into a file in force mode, and that dump file can be used to undo the operations from the from-file subcommand. Every one of those is a decision a person would not necessarily make, which is why the tool is usable on a directory tree that matters.
Installing it in three ways
There are four routes to a working binary and they cover the common cases. The GitHub releases page hosts compressed files for each platform, and the README notes that those archives contain the executable and additional content such as completion files for Bash, Zsh, fish and PowerShell. On Arch Linux a package is available in the AUR under the name rnr, installing the latest version. On macOS or Linux, Homebrew installs it with:
brew install rnrand for Rust developers there is a source route. From a git repository it is a clone and a cargo install:
git clone https://github.com/ismaelgv/rnr .
cargo installand from Crates.io it is a single command:
cargo install rnrThe Cargo manifest shows the package at version 0.5.1 on edition 2024, licensed MIT, categorised as a command line utility, with a small and telling dependency list. clap with the derive feature handles the argument parsing, regex supplies the matching, serde and serde_json handle the dump file format, walkdir does the recursion, and path_abs deals with path handling. Two dependencies stand out for what they reveal about the design. any_ascii at 0.3.0 is the UTF-8 to ASCII conversion feature, and rayon at 1.10.0 means the work is parallelised across cores, which matters when a recursive rename touches tens of thousands of files.
Renaming a list, and the two flags that change the scope
The basic invocation passes files as arguments and takes a replacement expression:
rnr regex -f file renamed ./file-01.txt ./one/file-02.txt ./one/file-03.txtThe -f flag forces the operation; without it rnr stays in its dry-run default. Here the expression is file and the replacement text is renamed. Directories are ignored by default and are included with the -D option, which the README shows as:
rnr regex -f -D foo bar ./*The replacement limit is the -l option, and its behaviour is worth understanding because it changes what the regex means. The limit is set to 1 by default, and it can be configured to replace multiple non-overlapping matches, with all matches replaced when the option is set to 0:
rnr regex -f -l 0 o u ./*On a file called foo.txt that produces fuu.txt, and on foofoofoo.txt it produces fuufuufuu.txt rather than a single substitution, because the limit allows the second and third occurrences to be replaced too. The recursive mode is -r, which walks the path without a depth limit unless a max depth is given, and the README's own tree examples show the resulting names changing at every level of the hierarchy.
Pipes into rnr, and the Windows wildcard problem
The tool is designed to sit at the end of a pipeline, and the README gives two worked examples. To find files older than one day and rename them:
find . -type f +mtime 1 | xargs rnr regex -f file renamedand to read a list of files from a file:
cat file_list.txt | xargs rnr regex -f file renamewith the list file holding one path per line, for example file-01.txt, one/file-02.txt and one/file-03.txt. That combination is where the tool earns its place, because the selection logic stays in find and the renaming stays in rnr, rather than being expressed in a language that has to do both. Two platform caveats are documented. On Windows, the examples that use a wildcard need the wildcard expanded in PowerShell, for example rnr regex a b (Get-Item ./*), and this is not supported in cmd.exe. And if the regular expression begins with a hyphen, the application parses it as an argument, so you need to use the -- separator after the flags and before the arguments, for example rnr regex -f -- '-foo' '-bar'.
Undo through the dump file
The dump file is the feature that turns a batch rename from a risky operation into a recoverable one. The README lists both create and read operations from dump file and undo operations from dump file as separate features, and the default behaviour section says that in force mode all operations are dumped into a file, and that this dump file can be used to undo those operations from the from-file subcommand. So the sequence is a dry run to preview, a forced run that records every rename it performs, and later an undo that reads the record back and reverses it. The format is serialised through serde with serde_json in the dependency list, which suggests the dump is JSON rather than a log format, which is good news for anyone who wants to inspect or process it. The undo and redo section of the README covers the from-file subcommand in detail, and the documentation navigation points there. The trade-off to be aware of is that the undo is only as good as the dump, so a run that was not forced leaves nothing to undo, and a run whose dump file is deleted leaves nothing to undo either.
Transformations, ASCII conversion and the other conveniences
Beyond a plain substitution, the feature list includes applying text transformations to the replacements including capture groups, and converting UTF-8 file names to an ASCII representation. The advanced examples section covers replacing extensions, replacing numbers, capturing groups, capturing several named groups and swapping them, and capturing several groups and applying a transformation, which is where the capture group support earns its keep. The ASCII conversion is a separate flag-driven operation for the case where a filename has characters that a particular filesystem or transfer path will not accept. There is also a select limit of replacements and the option to create backup files before renaming, and the feature list credits automated checks to avoid unwanted file collisions, removals or overwrites. The Rust source lives under src/ with a build.rs at the root, and the build dependencies are clap, clap_complete and clap_mangen, which is how the shell completions and man pages are generated at build time.
Editorial conclusion
Adopt rnr when you have a directory tree of files whose names follow a pattern you want to rewrite, and you want the change previewed, checked for collisions and reversible. Do not adopt it if you need renaming across remote hosts or a scripted API, since it is a local command line tool with a single set of subcommands. Verify first that your filenames are UTF-8 valid, because the tool only accepts those by default, that the regular expression syntax you want is supported by the regex crate, and that the replacement limit you set matches your intent, since the default of one will leave repeated matches alone.
Frequently asked questions
What does rnr do by default?
It checks all operations to avoid overwriting existing files, runs as a dry run, only accepts UTF-8 valid arguments and filenames, works on files and symlinks while ignoring directories, ignores hidden files and directories, generates no backups, sets the replacement limit to one and colours its output on ANSI terminals.
How do I undo a rename performed with rnr?
In force mode rnr dumps all operations into a file, and that dump file can be read back by the from-file subcommand to undo the operations. The dump is serialised through serde and serde_json, so it is a JSON document rather than a log file.
How do I install rnr?
Download a compressed binary from the GitHub releases page, which also carries completion files for Bash, Zsh, fish and PowerShell, install the AUR package on Arch Linux, run brew install rnr on macOS or Linux, or build it from source with cargo install rnr, or clone the repository and run cargo install.
How do I rename files recursively and replace every match?
The recursive option is -r, and it walks the path without a depth limit unless a max depth is given. The replacement limit is -l and it is set to 1 by default, while setting it to 0 replaces all non-overlapping matches, so rnr regex -f -l 0 o u ./* turns every o in a filename into a u.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ismaelgv-rnr)Was this useful?