CGamesPlay7 days ago
Since we're all giving replacements to this, nobody's mentioned my preferred one, so: use git add's patch mode. On a clean worktree, do the search/replace in bulk. Then use `git add --patch` to selectively add the good replacements and skip the bad ones. Finally, `git checkout -- .` to throw away all the bad ones. The nice part about this is that it's not much to remember. If you can make a global search and replace, and you can use `git add --patch` (which is useful loads of times), you can do a selective search and replace by combining them.
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.
pabs36 days ago
The `git gui` command is a nice-ish graphical alternative to `git add -p` and `git checkout` btw. You can even stage and discard individual lines. There are of course lots of other ones, but the TCL/Tk one is quite featureful.
augusto-moura6 days ago
Pretty neat trick, I will definitely use it in the future
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
fragmede7 days ago
be very careful with
git checkout .
it will eat your changes changes if you yolo it.sriram_malhar7 days ago
You get all this and more with a direct perl one liner. Without the interactivity. I'd argue that if it is a lot of files, interactivity would be a pain. Also, since the original is preserved as a .bak file, one can be fearless about trying
#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)
jmhobbs8 days ago
Very cool! I currently use `sad` for this, if you're already an fzf user you should check it out.
sigmonsays7 days ago
We're losing the art of bash ``` find -type f -iname '*.go' | xargs -r -n1 sed -i 's,foo,foobar,g' ```
asicsp7 days ago
Why use `xargs` instead of `-exec`? And if you do need `xargs` (for example, for parallel processing with `-P`), it is recommended to use `-print0` with `find` and `-0` with `xargs` to avoid issues due to filenames.
theamk7 days ago
xargs passes many inputs to one script invocation, so even with a single thread there is often a dramatic speedup.
(and agree re -print0/-0, it's absolutely essential)
asicsp7 days ago
`find` can do that as well with `{} +` (at least, the `GNU` implementation and it'll automatically add more invocations if there are just way too many files).
In any case, OP was using `-n1` which means one file per invocation.
mh-7 days ago
Something that frequently trips me up, mostly when helping colleagues, is the arguments to both find and xargs differ substantially between GNU and the FreeBSD-derived ones that ship on macOS.
lofaszvanitt7 days ago
The problem is... if you use these on rare occasions it gets frustrating, because you have to read the manual or google everything or ask the llm again and again.
noone can remember these abbreviations
KetoManx647 days ago
https://atuin.sh/ is such a huge productivity booster in these scenarios. I remember that I used find + xargs command sometime in the last 6 months on of my computers and with Atuin I can quickly find it and then slightly modify it to fit my current need.
fwip7 days ago
You can save useful snippets in a file for later use.
feelamee6 days ago
yes, but...
noone except your shell! Fish is perfect and very very often can guess what I want using pwd, file existence, current prompt, etc...
IshKebab7 days ago
Yeah it's great! Difficult to remember Bash pipelines are being replaced with modern tools with good UX.
sigmonsays2 days ago
I disagree, They are not difficult to remember.
you'll never build good UX and a powerful tool. You're either a master of your tool or you're not.
swah3 days ago
Love this but also could never really stick to similar tools (codemod, fastmod..)
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)
eevilspock8 days ago
A Homebrew install option will help this take off on Macs. https://github.com/thomasschafer/scooter/issues/6
rvz8 days ago
That was my first problem with trying to install this. But agree that it should be on Homebrew.
aerzen8 days ago
Cool.
I assumed it uses ripgrep (or the underlying walkdir) because that's the established high-performance tool for this. But apparently not.
tomschaferop8 days ago
It uses https://docs.rs/ignore/latest/ignore/ to walk dirs while respecting ignore files
seritools8 days ago
(And `ignore` uses `walkdir` internally)
burntsushi8 days ago
For single threaded use cases. For multi-threaded, it has its own parallel directory traversal. :-)
herrington_d7 days ago
Cool! is it possible to support structural search like ast-grep[1]? ast-grep has some interactive mode but it is nothing near Scooter.
rvz8 days ago
There was another comment about the difficulty in installing scooter and in the issues section, there are some requests to add more installation options.
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.
tpoacher8 days ago
Neat ... but I'd probably just do this by opening a "grep -l" list in nano for interactive replacement directly instead. Easy peasy.
guytv7 days ago
Am I the only one who thought this could alsop work lik "just" a find tool, only to discover it silently erased that word from all files in the directory without asking for confirmation? This felt riskier than rm -rf.
tomschaferop5 days ago
This should only happen if you hit enter on the confirmation screen - was it not clear that this would happen? I'd rather not slow users down by making them confirm that they definitely want to perform the replacement, but I'm happy to hear about other ways to make it clear that a replacement is going to be performed
level877 days ago
This looks great, good work.
I’ve been using serpl lately, https://github.com/yassinebridi/serpl
doylemark8 days ago
nice! Find and replace across a codebase is one of the few times I open an IDE.
Being able to interactively ignore instances for replacement is great!
anthk8 days ago
Similarly, on bash/ksh: set -o vi Ctrl-[ v (or ESC) set -o emacs Ctrl-x e
Freak_NL8 days ago
Am I alone in initially thinking this was specifically for the fish shell because of this tool's name?
darrenf8 days ago
Perhaps. I as a fish user thought “oh, like `string replace`”
mg8 days ago
You could also use vim in a loop. Say you want to replace "hello" in all files in the current dir with "world" and confirm every replace, then you would do:
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.[deleted]8 days agocollapsed
matt32108 days ago
Very nice, it might be a good alternative when I can't use vscode remote connections.
jph8 days ago
Excellent, thank you. I do this with sed & awk & sometimes an IDE, and scooter looks better in every way.
I'm adding scooter to my cargo install favorites:
jmercouris8 days ago
Feels like we just keep making tools that already exist in Emacs.
mway8 days ago
I dunno, seems reasonable to me that we might have nice things without requiring everyone to use emacs. (And for those who do use emacs, I guess you're ahead of the curve?)
mananaysiempre7 days ago
On the other hand, it seems reasonable that we should be able to have nice things without giving up our editors. I know I’ve been spoiled by Kakoune’s cursors, but this feels like a tool that should work by spawning $EDITOR in the middle of its execution (or perhaps just having two phases and a control file). I don’t know if that’s actually possible with the current capabilities of $EDITORs (which are not Emacs). I just feel, in the darkest hour of the night which I spend reflecting on UIs, like it should be.
alganet7 days ago
Nonsense, lots of people are doing text editors.
bloopernova8 days ago
A useful feature of bash and zsh is the "edit command". The standard shortcut is "ctrl-x ctrl-e".
It opens the current command line in $EDITOR, which often defaults to vim.
indentit6 days ago
I tried this out, and nothing happened. Then I discovered that it has to be enabled first, for example by: https://unix.stackexchange.com/a/34251/152147 Thanks for sharing, learnt something new :)
dmd8 days ago
That is very useful. What does it have to do with this?
bloopernova8 days ago
If you want to search and replace a command line, there's tools to do it in your favourite editor.
dmd8 days ago
Ah, so you didn't click through and actually see what this tool is, you just read the title.
bloopernova8 days ago
I did click through, but misinterpreted what it was doing. Apologies, I'm "multitasking".
[deleted]8 days agocollapsed
agateau8 days ago
Looks handy!
patatass7 days ago
Couldn't find it in nixpkgs.
flowingfocus7 days ago
I opened a PR for it: https://github.com/NixOS/nixpkgs/pull/356310
In the meantime you can also add packages that aren't yet in nixpkgs using pkgs.callPackage.
gurgeous8 days ago
Also see the excellent https://github.com/your-tools/ruplacer.
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
jmarcher8 days ago
In Emacs, there is [helm-ag-edit](https://github.com/emacsorphanage/helm-ag) (but uses ripgrep if present). It's almost identical to your workflow, but all done inside the same app.
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.
[deleted]8 days agocollapsed
dsjkvf8 days ago
[flagged]
[deleted]8 days agocollapsed
tomschaferop8 days ago
I use Helix, which doesn't have find and replace built in!
colordrops8 days ago
What's your workflow with vim?
gjvc8 days ago
[flagged]
[deleted]8 days agocollapsed
kiriberty8 days ago
[flagged]
[deleted]8 days agocollapsed
oulipo8 days ago
I'm using this quickly put-together shell script called replace
#!/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."
JadeNB7 days ago
What is gum?
oulipo7 days ago
it's a cool helper for shell scripts, do have beautiful interfaces, check it here https://github.com/charmbracelet/gum
[deleted]8 days agocollapsed
taydeskj8 days ago
[flagged]
oguz-ismail8 days ago
[flagged]
horsawlarway8 days ago
Yes. It takes literally seconds, and is a single command on basically every platform.
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...
oguz-ismail8 days ago
> Yes
Thanks, not interested then
fallingsquirrel8 days ago
sfinx8 days ago
Why not just ask politely instead?
lopkeny12ko8 days ago
[flagged]
dang8 days ago
Can you please not post shallow dismissals of other people's work? This is in the site guidelines: https://news.ycombinator.com/newsguidelines.html.
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):
karanbhangui8 days ago
"For a Linux user, you can already build such a system yourself quite trivially by getting an FTP account, mounting it locally with curlftpfs, and then using SVN or CVS on the mounted filesystem"
tomschaferop8 days ago
Not affiliated, I just built a little tool to make my life easier and thought I'd share
dang8 days ago
It's great and clearly the community appreciates it! I'll put Show HN in the title since that's the convention for sharing one's projects on HN (https://news.ycombinator.com/showhn.html).
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.
sockaddr8 days ago
I think it's cool. Thanks for sharing
[deleted]8 days agocollapsed