ren is a fast bulk file renamer, sibling of rep.
Where rep rewrites file contents, ren rewrites file names. Plain and regex find/replace, smart preserve-case rewrites, an interactive preview, glob filters, listing, and multiple -e/--expression rewrites in a single pass.
ren is dry-run by default - every invocation prints the plan and exits. Pass -W (or --write) when you're ready to actually rename.
brew install gechr/tap/renscoop bucket add gechr https://github.com/gechr/scoop-bucket
scoop install gechr/rencargo install --git https://github.com/gechr/renExamples below show the plan - add -W to commit.
# Replace `foo` with `bar` anywhere in basenames (cwd, files only)
ren foo bar
# Same, but actually rename
ren -W foo bar
# Recurse into subdirectories
ren -R foo bar
# Also rename matching directories
ren --include-dirs foo bar
# Restrict to .rs files
ren -f rs old new
# Stem-only matching is the default (api_v1.json -> api_v2.json)
ren v1 v2
# Include extensions in matching and replacement
ren -x rs txt
# Match only the extension; preserve the stem (foo.rs -> foo.txt)
ren -X rs txt
# Rename and change the extension in one pass (img_a.jpg, img_b.jpg -> 1.png, 2.png)
ren -W -s '{n}' -E png
# Smart-rename across case variants
# foo_bar -> hello_world, FooBar -> HelloWorld, FOO_BAR -> HELLO_WORLD, β¦
ren --smart foo_bar hello_world
# Regex rename: replace test_ prefix with spec_
ren --regex '^test_' 'spec_'
# Interactive preview: walks each entry and applies the accepted ones
ren --preview foo bar
# Apply multiple replacements in a single pass
ren -W -e foo bar -e baz qux
# Print matching paths only (no rename)
ren -l foo
# Case-only rename (works on case-insensitive filesystems too)
ren -W tmp TMP
# Number every file in cwd: 01_alpha.txt, 02_beta.txt, β¦ (smart per-dir width)
ren --prepend '{N}_'
# Custom counter format with explicit zero-padding and a path scope
ren --prepend='{n:03}_' src/
# Number files as a suffix on the stem: foo.txt -> foo-1.txt, β¦
ren --append '-{n}'
# Replace each name outright: 01.jpg, 02.jpg, β¦ (extension preserved)
ren --supplant '{n:02}'
# Lowercase every basename in cwd
ren --lower
# Compose: find/replace, then lowercase, then append, then prepend
ren --prepend '{N}_' --lower foo barWhen <find> <replace> alone isn't enough, transforms layer over the find/replace stage. They're optional, compose with each other, and make <find> <replace> itself optional too - so ren --prepend '{N}_' is a valid invocation.
| Flag | Effect |
|---|---|
-L, --lower |
lowercase the basename (mutex with --upper) |
-U, --upper |
uppercase the basename |
-P, --prepend <fmt> |
prepend a literal string or template |
-A, --append <fmt> |
append a literal string or template |
-s, --supplant <fmt> |
replace the name with a literal string or template (extension preserved unless combined with -x) |
-E, --change-extension <ext> |
change, add, or strip the file extension (composes with the stem pipeline; mutex with -x/-X) |
Counter templates (recognised inside --prepend, --append, and --supplant):
{n}- the 1-based per-parent-directory index (1,2,3, β¦).{n:0WIDTH}- zero-padded toWIDTHdigits ({n:03}β001,002, β¦).{N}- smart-width: zero-padded tomax(2, len(dir_count)). So a directory with under 100 entries gets01,02, β¦; 100+ gets001,002, β¦; 1000+ gets0001,0002, β¦. Pick this when you don't want to count by hand.
Anything else in the format string passes through, so [{n:02}]-, chapter-{n:03}_, -{n} all work as written. Both affixes share the same per-record counter, so --prepend '{n}-' --append '-{n}' produces 1-foo-1, 2-bar-2, β¦. Values that start with - need the =-attached form (e.g. --append='-{n}') so they aren't mistaken for flags.
The pipeline runs in fixed canonical order regardless of argv order:
- find/replace (positional or
-e) --supplant--loweror--upper--append--prepend
By default the pipeline runs on the file stem only and the extension is reattached afterward - this prevents accidents like ren txt notes rewriting report.txt to report.notes. -x/--include-extension opts back into matching the full basename, and -X/--only-extension flips the split so the pipeline runs on the extension only and the stem is preserved verbatim. -E/--change-extension <ext> is orthogonal: it overrides the reattached extension while the rest of the pipeline still runs on the stem, so it composes with --supplant and friends (ren -s '{n:02}' -E png β 01.png, 02.png, β¦). A leading dot is optional, files without an extension gain one, and an empty value (-E '') strips it; the whole extension is replaced, including compound ones (archive.tar.gz β archive.zip). It is mutually exclusive with -x/-X. Counter indexes reset per parent directory - with --recursive, each directory starts at 1 (or 01, 001, β¦ with a width specifier). Files filtered out by find/replace don't consume a counter slot.
ren --preview walks each plan entry and asks for a decision:
| Key | Action |
|---|---|
y |
accept this rename |
n |
reject this rename |
A |
accept this and all remaining renames |
q |
stop prompting and apply accepted renames |
β |
go back and revise the previous decision |
β |
skip this rename |
Accepted renames are applied immediately - --preview is its own mode and is mutually exclusive with --dry-run/--write.