| Crates.io | fnr-tool |
| lib.rs | fnr-tool |
| version | 0.1.0 |
| created_at | 2025-09-06 15:20:33.055006+00 |
| updated_at | 2025-09-06 15:20:33.055006+00 |
| description | A blazingly fast file and directory renaming tool with gitignore support |
| homepage | |
| repository | https://github.com/PatWiePersonal/fnr |
| max_upload_size | |
| id | 1827177 |
| size | 48,315 |
Because life's too short to rename files one by one like a caveman
fnr is a blazingly fastβ’ file and directory renaming tool that will make you question why you ever used mv in a for loop like some kind of shell script peasant. It's like sed for filenames, but with more colors, gitignore support, and less existential dread.
Have you ever had 47 files named component_something.rs and needed to rename them all to ui_something.rs? Have you ever stared at a directory full of test_*.py files and wished they were spec_*.py instead? Have you ever wanted to batch rename files without writing a bash script that looks like it was written by a caffeinated squirrel?
This is your salvation.
cargo install fnr-tool
# Or clone this repo and `cargo build --release` like the cool kids do
# Rename all files containing "old" to "new"
fnr "old" "new"
# Preview what would happen (because trust issues)
fnr "old" "new" --dry-run
# Use specific glob patterns
fnr "component" "ui" "**/*.rs"
# Multiple glob patterns (the power move)
fnr "test" "spec" "**/*.py" "**/*.js" "!node_modules/**"
# With base directory
fnr "old" "new" "**/*.rs" --base-dir /path/to/project
# Match multiple file types at once
fnr "component" "ui" "*.{rs,ts,js}" "*.toml"
# Exclude specific directories
fnr "old" "new" "**/*" "!target/**" "!node_modules/**"
# Search from a different base directory
fnr "config" "settings" "**/*.ini" --base-dir /path/to/configs
# Control search depth
fnr "test" "spec" "**/*.py" --max-depth 3 --min-depth 1
# Use regex because you're fancy
fnr --regex "test_(.+)" "spec_$1" "**/*.py"
# Rename all your poorly named components
fnr --regex "component_(.+)" "ui_$1" "src/**/*.rs"
By default, fnr will ask you about each rename because it respects your trust issues:
./src/old_component.rs -> ./src/new_component.rs
Replace filename/dirname? [Y]es/[n]o/[a]ll/[q]uit:
Just press a single key (no Enter required, we're not animals):
y - Yes, rename this filen - No, skip this onea - Yes to ALL remaining files (YOLO mode)q - Quit and pretend this never happened--dry-run # See what would happen without commitment
--no-interactive # YOLO mode (renames everything without asking)
--regex # Enable regex patterns for the power users
--type=file # Only rename files
--type=dir # Only rename directories
--type=both # Rename everything (default)
--no-recursive # Stay in current directory like a hermit
--case-sensitive # Because "Test" β "test" (obviously)
--hidden # Include hidden files (the secret ones)
--no-color # Remove all joy from your terminal
--no-symlink # Don't follow symbolic links
--no-skip-gitignore # Ignore .gitignore files (chaos mode)
--max-depth N # Maximum directory depth to search
--min-depth N # Minimum directory depth to search
--base-dir PATH # Base directory to search from
# You have: component_button.rs, component_input.rs, component_modal.rs
# You want: ui_button.rs, ui_input.rs, ui_modal.rs
fnr "component" "ui" "**/*.rs"
# Convert test_*.py to spec_*.py because you're fancy now
fnr "test_" "spec_" "**/*.py"
# Rename across multiple file types at once
fnr "old_api" "new_api" "**/*.{rs,ts,js,py}" "**/*.toml" "!target/**"
# Convert CamelCase to snake_case (sort of)
fnr --regex "([A-Z])" "_$1" --case-sensitive "**/*.rs"
# Rename everything with "old" to "new" in the entire project
fnr "old" "new" "**/*" --no-interactive
# (Use with caution, we are not responsible for your life choices)
# Respects .gitignore by default (like a civilized human)
fnr "component" "ui" "**/*.rs"
# Chaos mode: ignore .gitignore
fnr "component" "ui" "**/*.rs" --no-skip-gitignore
"**/*.rs" "**/*.toml" "!target/**" works like magic.gitignore automatically--max-depth and --min-depth for precisionfnr [OPTIONS] <PATTERN> [REPLACEMENT] [GLOB_PATTERNS...] [--base-dir BASE_DIR]
# Single pattern
fnr "old" "new" "**/*.rs"
# Multiple patterns
fnr "component" "ui" "**/*.rs" "**/*.ts" "**/*.js"
# With exclusions
fnr "test" "spec" "**/*.py" "!venv/**" "!__pycache__/**"
# Different base directory
fnr "config" "settings" "**/*.ini" --base-dir /path/to/configs
# Brace expansion support
fnr "old" "new" "*.{rs,toml,lock}"
fnr respects .gitignore files by default! This means:
target/, node_modules/, or .git/--no-skip-gitignore if you want to live dangerouslyglobset for efficient simultaneous pattern matchingignore crate for fast, gitignore-aware walkingfor file in *.txt; do mv "$file" "${file%.txt}.bak"; done more than oncerename is a Perl script (it is, and that's the problem)node_modules and target directories every timeFound a bug? Want a feature? Think the colors are wrong? Open an issue or PR!
Just remember: this tool was built by someone who got tired of renaming files manually, so the bar for "useful contribution" is pretty low.
MIT - Because sharing is caring, and lawyers are expensive.
fnr is not responsible for:
Made with β€οΈ and an unhealthy obsession with file organization and rational fear of the sed command
Remember: With great power comes great responsibility. Use --dry-run first, kids.