#!/usr/bin/env bash
# Function to escape special characters for sed
escape_sed_string() {
printf '%s\n' "$1" | gsed -e 's/[]\/$*.^[]/\\&/g'
}
help() {
gum style --foreground cyan --italic "\
Usage (everything optional, you will be prompted):\n\
$0\n\
--ext .js --ext .ts\n\
--from \"source string\"\n\
--to \"replacement string\"\n\
--dir somePath"
}
# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-h)
help
exit 0
;;
--help)
help
exit 0
;;
--ext) EXTENSIONS+=("$2"); shift ;;
--from) REPLACE_FROM="$2"; shift ;;
--to) REPLACE_TO="$2"; shift ;;
--dir) DIRECTORY="$2"; shift ;;
*) gum style --foreground red --bold "Unknown parameter: $1"; exit 1 ;;
esac
shift
done
# Check for missing parameters and prompt using gum
if [ -z "${EXTENSIONS+set}" ]; then
EXTENSIONS=($(gum choose \
--no-limit \
--selected .ts,.mts,.tsx,.vue,.js,.cjs,.mjs \
.ts .mts .tsx .vue .js .cjs .mjs .txt .md .html .json))
fi
# Exit if no extension is selected
if [ ${#EXTENSIONS[@]} -eq 0 ]; then
gum style --foreground red --bold " Error: No extensions selected. Exiting."
exit 1
fi
if [ -z "${REPLACE_FROM+set}" ]; then
REPLACE_FROM=$(gum input --placeholder "Search string:")
if [ -z "${REPLACE_FROM}" ]; then
echo "No replace from string, exiting"
exit 1
fi
fi
if [ -z "${REPLACE_TO+set}" ]; then
REPLACE_TO=$(gum input --placeholder "Replace string:")
fi
if [ -z "${DIRECTORY+set}" ]; then
DIRECTORY="."
fi
# Escape strings for sed
ESCAPED_FROM=$(escape_sed_string "$REPLACE_FROM")
ESCAPED_TO=$(escape_sed_string "$REPLACE_TO")
# Run the replacement
for ext in "${EXTENSIONS[@]}"; do
gum style --foreground blue " Replacing ${ext} files..."
find "$DIRECTORY" -type f -name "*$ext" ! -path "*/node_modules/*" -exec gsed -i "s/$ESCAPED_FROM/$ESCAPED_TO/g" {} \;
done
gum style --foreground green --bold " Replacement complete."
It opens the current command line in $EDITOR, which often defaults to vim.
If you're afraid of that - are you realistically going to be running a cli interactive find and replace?
If you want to package up an EXE for your specific system and host it somewhere - the license is MIT, go right ahead...
For more advanced needs, I have a custom thing called greprep that let's you make changes using your favorite editor. Workflow is like this:
1. $ rg -n .... > /tmp/lines.txt
2. (edit lines.txt in vscode)
3. $ greprep /tmp/lines.txt to apply the changes
Thanks, not interested then
It's important, when people share something they've made on HN, that they don't run into this sort of bilious internet putdown.
Edit - these are other examples of the same thing (i.e. the thing we don't want in HN threads, and which we'd appreciate if you'd not do any more of):
Btw, do you want to include some text giving the backstory of how you came to work on this, and explaining what's different about it? that's also the convention. If you post it in a reply to this comment, I'll move your text to the top of the thread.
1. helm-ag <pattern> # the search results are updated as you type 2. helm-ag-edit # edit the search result as regular text. Use multi-cursors, macros, whatever. 3. helm-ag-edit-save # commits the changes to the affected files
All those commands have keybindings, so it's pretty fast. I'll often open up Emacs just to do that and then go back to my JetBrains IDE.
for f in $(grep -l 'hello' *); do vim -c ':%s/hello/world/gc | wq' "$f"; done
Or if you want to use some more vim magic, this simpler command will do the same: vim -c "argdo %s/hello/world/gce | update" -c "qall" *
"argdo" will do the replace command for each file, the additional e modifier in gce will ignore files that do not contain the search string and the second command "qall" is to quit vim after the work is done.I'm adding scooter to my cargo install favorites:
https://github.com/thomasschafer/scooter/issues/6
Not everyone has the Rust toolchain installed on their machine. The `cargo install` installation directive needs to be discouraged.
#change xxx to yyy in all html
bash> perl -pi.bak -e 's/xxx/yyy/g' *.html
#change xxx10 (say) to yyy10 in all html
bash> perl -pi.bak -e 's/xxx(\d+)/yyy$1/g' *.html
# Change x4 to yyyy, where the number of y's equals to the number after x.
bash> perl -pi.bak -e 's/x(\d+)/"y" x $1/ge' *.
The last example shows the /e operator, which evaluates an expression and uses the result as substitute, instead of a simple string.And finally, to exclude files, one can use a subshell. For example, suppose you want to change all html, but exclude undesirable.html..
perl -pi.bak -e 's/x/y/g' $(ls *.html | egrep -v undesirable)
As far as this actual tool: the demo GIF is way too fast-paced to show what's going on. A better demo would maybe search "ring", have 10 or so results instead of pages and pages, and show how you can unselect "spring" matches which were unintentionally caught.
In the meantime you can also add packages that aren't yet in nixpkgs using pkgs.callPackage.
noone can remember these abbreviations
I’ve been using serpl lately, https://github.com/yassinebridi/serpl
But it only works in git repositories. Sometimes you need to search and replace on non-git repositories, for those cases Scooter might be the answer
Main change I would need I think: to assume that if no replacement if provided, the user wants to edit each instance individually "inline" (or dropping into vim, not sure what works best - this model breaks once you want to swap lines, replace 1 word with multiple lines etc).
And ofc accept command line args for the initial search string (and optionally replacement) from command line so the first screen is skipped! (I think folder could be optional)
(I also tried to undo my changes after deleting words by accident but thats a hard ask! :D)
you'll never build good UX and a powerful tool. You're either a master of your tool or you're not.