[HN Gopher] Show HN: Scooter - Interactive find and replace in t...
       ___________________________________________________________________
        
       Show HN: Scooter - Interactive find and replace in the terminal
        
       Author : tomschafer
       Score  : 122 points
       Date   : 2024-11-15 16:45 UTC (1 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | lopkeny12ko wrote:
       | [flagged]
        
         | karanbhangui wrote:
         | "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"
         | 
         | https://news.ycombinator.com/item?id=8863
        
         | tomschafer wrote:
         | Not affiliated, I just built a little tool to make my life
         | easier and thought I'd share
        
           | sockaddr wrote:
           | I think it's cool. Thanks for sharing
        
           | dang wrote:
           | 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.
        
         | dang wrote:
         | 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):
         | 
         | https://news.ycombinator.com/item?id=41810426
         | 
         | https://news.ycombinator.com/item?id=41224056
        
       | oulipo wrote:
       | 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."
        
         | JadeNB wrote:
         | What is gum?
        
           | oulipo wrote:
           | it's a cool helper for shell scripts, do have beautiful
           | interfaces, check it here
           | https://github.com/charmbracelet/gum
        
       | agateau wrote:
       | Looks handy!
        
       | bloopernova wrote:
       | 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.
        
         | dmd wrote:
         | That is very useful. What does it have to do with this?
        
           | bloopernova wrote:
           | If you want to search and replace a command line, there's
           | tools to do it in your favourite editor.
        
             | dmd wrote:
             | Ah, so you didn't click through and actually see what this
             | tool is, you just read the title.
        
               | bloopernova wrote:
               | I did click through, but misinterpreted what it was
               | doing. Apologies, I'm "multitasking".
        
       | aerzen wrote:
       | Cool.
       | 
       | I assumed it uses ripgrep (or the underlying walkdir) because
       | that's the established high-performance tool for this. But
       | apparently not.
        
         | tomschafer wrote:
         | It uses https://docs.rs/ignore/latest/ignore/ to walk dirs
         | while respecting ignore files
        
           | seritools wrote:
           | (And `ignore` uses `walkdir` internally)
        
             | burntsushi wrote:
             | For single threaded use cases. For multi-threaded, it has
             | its own parallel directory traversal. :-)
        
       | gurgeous wrote:
       | 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
        
         | jmarcher wrote:
         | 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.
        
       | doylemark wrote:
       | 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!
        
       | Freak_NL wrote:
       | Am I alone in initially thinking this was specifically for the
       | fish shell because of this tool's name?
        
         | darrenf wrote:
         | Perhaps. I as a fish user thought "oh, like `string replace`"
        
       | matt3210 wrote:
       | Very nice, it might be a good alternative when I can't use vscode
       | remote connections.
        
       | eevilspock wrote:
       | A Homebrew install option will help this take off on Macs.
       | https://github.com/thomasschafer/scooter/issues/6
        
         | rvz wrote:
         | That was my first problem with trying to install this. But
         | agree that it should be on Homebrew.
        
       | mg wrote:
       | 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.
        
       | jph wrote:
       | 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:
       | 
       | https://github.com/sixarm/cargo-install-favorites
        
       | jmercouris wrote:
       | Feels like we just keep making tools that already exist in Emacs.
        
         | mway wrote:
         | 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?)
        
           | mananaysiempre wrote:
           | 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.
        
         | alganet wrote:
         | Nonsense, lots of people are doing text editors.
        
       | jmhobbs wrote:
       | Very cool! I currently use `sad` for this, if you're already an
       | fzf user you should check it out.
       | 
       | https://github.com/ms-jpq/sad
        
       | anthk wrote:
       | Similarly, on bash/ksh: set -o vi Ctrl-[ v (or ESC) set -o emacs
       | Ctrl-x e
        
       | rvz wrote:
       | 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.
        
       | tpoacher wrote:
       | Neat ... but I'd probably just do this by opening a "grep -l"
       | list in nano for interactive replacement directly instead. Easy
       | peasy.
        
       | herrington_d wrote:
       | Cool! is it possible to support structural search like ast-
       | grep[1]? ast-grep has some interactive mode but it is nothing
       | near Scooter.
       | 
       | 1: https://ast-grep.github.io/
        
       | patatass wrote:
       | Couldn't find it in nixpkgs.
        
         | flowingfocus wrote:
         | 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.
        
       | sigmonsays wrote:
       | We're losing the art of bash ``` find -type f -iname '*.go' |
       | xargs -r -n1 sed -i 's,foo,foobar,g' ```
        
         | mh- wrote:
         | 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.
        
         | asicsp wrote:
         | 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.
        
           | theamk wrote:
           | 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)
        
             | asicsp wrote:
             | `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.
        
         | lofaszvanitt wrote:
         | 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
        
           | KetoManx64 wrote:
           | 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.
        
           | fwip wrote:
           | You can save useful snippets in a file for later use.
        
         | IshKebab wrote:
         | Yeah it's great! Difficult to remember Bash pipelines are being
         | replaced with modern tools with good UX.
        
       | sriram_malhar wrote:
       | 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)
        
       | CGamesPlay wrote:
       | 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.
        
         | fragmede wrote:
         | be very careful with                   git checkout .
         | 
         | it will eat your changes changes if you yolo it.
        
         | augusto-moura wrote:
         | 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
        
       | level87 wrote:
       | This looks great, good work.
       | 
       | I've been using serpl lately,
       | https://github.com/yassinebridi/serpl
        
       | guytv wrote:
       | 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.
        
       ___________________________________________________________________
       (page generated 2024-11-16 23:02 UTC)