| Crates.io | fnr |
| lib.rs | fnr |
| version | 0.1.0 |
| created_at | 2021-12-29 09:11:52.053982+00 |
| updated_at | 2022-01-06 17:45:35.075311+00 |
| description | Intuitive find and replace. |
| homepage | |
| repository | https://github.com/erik/fnr |
| max_upload_size | |
| id | 504715 |
| size | 73,987 |
Like find ... | xargs sed ..., but with a memorable interface.
Recursively find and replace patterns in files and directories.
fnr [OPTIONS] FIND REPLACE [PATH...]
fnr is intended to be more intuitive to use than sed, but is not a
drop in replacement. Instead, it's focused on making bulk changes in a
directory, and is more comparable with your IDE or editor's find and
replace tool.
fnr is alpha quality. Don't use --write in situations you
wouldn't be able to revert.
Replace "old_function" with "new_function" in current directory.
fnr old_function new_function
Choose files and directories to consider.
fnr 'EDITOR=vim' 'EDITOR=emacs' ~/.zshrc ~/.config/
We can use --literal so the pattern isn't treated as a regular expression.
fnr --literal 'i += 1' 'i++'
Replace using capturing groups.
fnr 'const (\w+) = \d+;' 'const $1 = 42;'
Use -W --write to write changes back to files.
fnr --write 'Linus Torvalds' 'Linux Torvalds'
Use -I --include to only modify files or directories matching a pattern.
fnr --include 'Test.*\.kt' 'mockito' 'mockk'
Similarly, use -E --exclude to ignore certain files.
fnr --exclude ChangeLog 2021 2022
Files and directories to consider can also be given over standard input.
find /tmp/ -name "*.csv" -print | fnr "," "\t"
Use -p --prompt to individually accept or reject each replacement.
fnr --prompt --literal 'i++' '++i'
--- ./README.md: 2 matching lines
- 18: $ fnr --literal 'i += 1' 'i++'
+ 18: $ fnr --literal 'i += 1' '++i'
Stage this replacement [y,n,q,a,e,d,?] ?
cargo install fnr
If you'd prefer to build from source instead:
$ git clone git@github.com/erik/fnr.git
$ cd fnr
$ cargo install --path .
Built on top of ripgrep's path traversal and pattern matching, so even though performance isn't an explicit goal, it's not going to be the bottleneck.
In fact, without writing changes back to files, it's imperceptibly slower than ripgrep itself.
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
rg "EINVAL" ./linux |
510.4 ± 25.8 | 467.6 | 555.0 | 1.00 |
fnr "EINVAL" "ERR_INVALID" ./linux |
620.4 ± 22.1 | 573.7 | 649.7 | 1.22 ± 0.08 |
fnr --write "EINVAL" "ERR_INVALID" ./linux |
3629.0 ± 76.2 | 3538.0 | 3802.3 | 7.11 ± 0.39 |
ag "EINVAL" ./linux |
2560.0 ± 43.6 | 2518.4 | 2668.1 | 5.02 ± 0.27 |
grep -irI "EINVAL" ./linux |
37215.8 ± 7444.7 | 31316.1 | 49096.6 | 72.92 ± 15.04 |
If fnr doesn't quite fit what you're looking for, also consider:
fnr.