Crates.io | molybdenum |
lib.rs | molybdenum |
version | 0.1.10 |
source | src |
created_at | 2021-05-26 13:19:47.441988 |
updated_at | 2024-06-13 16:06:58.986032 |
description | Recursive search and replace CLI application |
homepage | |
repository | https://github.com/gfannes/molybdenum |
max_upload_size | |
id | 402275 |
size | 67,965 |
Recursive, line-based search and replace CLI application.
cargo install molybdenum
mo -h
should print its help and versiongit clone https://github.com/gfannes/molybdenum
cargo install --path molybdenum
mo -h
should print its help and versionFollowing commands demonstrate how mo
can be used to accomplish different tasks:
mo
: When no search pattern is specified, only the filenames are listedmo -l
: Explicitly ask to output only the filenamesmo -C FOLDER
: Use FOLDER as root for searchingmo -e hpp -e cpp
: Only take files with hpp
and cpp
extension into accountmo -f PART
: Keep filenames that match against PARTmo -F PART
: Keep filenames that do not match against PARTmo -0
: Use 0x00
to separate filenames. This is handy when using the output with xargs
.mo -u -U -a
: Take hidden files, folders and binary files into account as wellmo PATTERN
: Search for PATTERN in files recursivelymo -p PATTERN
: Search for PATTERN in files recursivelymo -w PATTERN
: Search for PATTERN in files recursively, adding word-boundary constraints arround PATTERNmo -s PATTERN
: Search for PATTERN, case-sensitivemo -B 10 -A 10 PATTERN
: Output a context of 10 additional lines before and after each matchmo needle -w -r naald -n
: Simulate the replacement of the the word needle
with the Dutch word naald
mo needle -w -r naald
: Really replace the word needle
with the Dutch word naald
xargs
mo -l -C FOLDER -0 | xargs -0 -r mo -i PATTERN
: Note the -i
option to ensure mo
will search in files and not Stdin. In addition, the xargs -r
option should be set to ensure nothing will run if no filepaths are produced.Next to this, mo
detects if input comes from a console or redirection, and will act accordingly, as well as for its output: mo
can be used to report or make replacements in a piped stream as well.
Following bash
functions allows you to open a file (o) or change to a folder (c) based on the fuzzy search functionality of fzf. You can pass them any argument that mo
accepts, making them handy interactive tools. They rely on bat to provide a preview, and nvr to open the selected file in a new or already running instance of neovim, and zoxide to register and track your most popular folders.
# Open file using `bat` as preview
o() {
mo -l $* | fzf --multi --preview 'bat --style=numbers --color=always --line-range :500 {}' --preview-window 'right:60%' | xargs -I % nvr --remote-tab %
}
# Open file using `mo` as preview
s() {
export all_args="$*"
mo -l $* | fzf --multi --preview 'mo -c -i ${all_args} -C {}' --preview-window 'right:60%' | xargs -I % nvr --remote-tab %
}
# Change to dir using `z`
c() {
z `mo -L $* | fzf`
}
Powerful search can be found without problems, eg, grep, ack, ag, ripgrep or broot.
Tools for replacing recursively in a folder are more difficult to find, although some exist: fart-it. Typically, people use a combination of searching, xargs and a replacement tool like sed or rpl.
I use code searching a lot to investigate a large source code base before attempting a replace. Even with 100k files, search is fast and fairly easy. Recursively replacing text is much more dangerous, especially if it requires the combination of several less frequently used tools; it's difficult to remember a search-xargs-replace
combination if not used on a daily basis. On top of this, the search
tool used to filter the set of files and perform a dry-run, is not per-se using the same search query as the replace
tool. After all, these are different tools. It would be better if a single tool could be used for every-day searching and replacing. This is exactly what The Molybdenum Replacer intends to achieve.
In addition, I find it difficult to use ag
and rg
to filter against the file path and search for content in the remaining files; the -g
option for ag
and rg
is confusing in my opinion. As such, I would expect ag -g /auro/ -w test
to search for the word test
in all files that have /auro/
in their path, but that is not what actually happens. It filters with /auro/
and test
against the filename (or something that looks like it).
The real reason, of course, is that I had some free time and was looking for a nice project/excuse to learn rust.
Following features are implemented and usable in the current version:
.gitignore
files-L
optionFollowing features might be added sooner or later:
-
-n
(and use a different name)-c
mo -ws test
0x0a
.mo
is currently single-threaded. To achieve ripgrep-like performance, all CPU's are probably required.-l
is used to only output filenames, mo
can stop searching after the first match.wc
molybdenum
, and it is very hard to type. mo
is better, but difficult to search on the internet.Scenario: count all files
Running `mo -l -u -U -a | wc`
Elapsed time: 0:00.29
Output: 184675 187146 16959297
Running `rg --files -uu -a | wc`
Elapsed time: 0:00.15
Output: 184675 187146 16589947
Running `ag -l -uu -a | wc`
Elapsed time: 0:00.66
Output: 184675 187146 16589947
Scenario: search for word `test` in .cpp files in subfolder `core` where path contains /auro/
Running `mo -C core -e cpp -f /auro/ -w -p test -l | wc`
Elapsed time: 0:00.03
Output: 165 165 9008
Running `rg -t cpp --files core | rg /auro/ | rg '.cpp$' | tr '\n' '\0' | xargs -0 rg -i -w test -l | wc`
Elapsed time: 0:00.01
Output: 165 165 9008
Running `ag --cpp -l . core | ag /auro/ | ag '.cpp$' | tr '\n' '\0' | xargs -0 ag -w test -l | wc`
Elapsed time: 0:00.05
Output: 165 165 9008
I don't know how I can accomplish this scenario with ag
and rg
in a single command without relying on xargs
and tr
.
.out
extension to the list of binary files-f
and -F
is now case-sensitive only when -s
is set-m
. This should be replaced with proper Regex submatch support.-P %
is specified, the replacement string specified via -r
will substitute capture groups for all occurences of %
or %[0-9]
.$EDITOR
using the -o
option. Best used in combo with -l
.-R
option