Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Rep and Ren: A new approach to CLI find, replace, and renaming (robenkleene.com)
111 points by robenkleene on Dec 30, 2023 | hide | past | favorite | 22 comments


This is clever. I wrote a "replace_all" bash function years ago that does the "run ripgrep and replace on the result files" and while it works well 99% of the time it is annoying when it screws up things due to partial matches. I solved this by basically forcing literal matches and word boundaries but it's limiting at that point.

The rep approach to use the actual ripgrep output seems very clever, tho I wonder how well it works in practice, but I'll be sure to give it a try.


Piece by piece, we reinvent Perl, then the new Perl turns into a monstrosity, we abandon it, and the cycle begins anew.


Perl oneliners never got old. A good collection of useful scripts can only win from new tooling. We honour perl with the P in PCRE and yet have modern tools like https://beyondgrep.com/feature-comparison/


`rg | rep` still requires repeating the pattern. 99% of the time, all I need is a simple replacement on checked-in files[1] and for that I've had `git-gsr` in my PATH for a decade or more:

https://gist.github.com/jaysoffian/0eda35a6a41f500ba5c458f02...

Of course this also repeats the pattern, but that's an implementation detail I let the script handle for me. A future improvement to `rep` could be for it to invoke `rg` directly in order to avoid needing to repeat the pattern, though I suppose that could be a shell script easily enough.

[1]: How often are folks doing a global search and replace on a mass of files that isn't checked-in to a repo? I've been a sys admin and programmer for 25+ years and I think I can count on one hand the times I've ever needed to do that. If I had to do it today, I'd very likely create a temporary repo first in order to have a quick way to verify the diff and restore to previous state if need be.


I use "sad" for this: https://github.com/ms-jpq/sad

  git ls-files **/*.lua | sad foo bar


This is...brilliant.

As a vim user, I have never really gotten into the quick fix list. I want to do my stuff in the command line.

But I find that a lot of new command line tools don't match my thinking, so I struggle to use them. (One example is fzf, which it seems people love.)

I do love ripgrep, though. I also love tools where I can see what action it performs before actually doing it. I use `-n` on ZFS all the time.

But making the action itself require a flag? Brilliant. Using stsndard streams? Brilliant. Showing what it will do, not the end result? Brilliant.

I'm going to download and and start experimenting right away.


It might be worth noting that while ripgrep doesn't support in-place file editing, it's still possible to replace matches using the `-r` flag, couple that with a tool like `sponge` from moreutils[0] and you can effectively perform search and place like such:

    $ rg --passthru search -r replace file.txt | sponge file.txt
And preview changes like this:

    $ rg --passthru search -r replace file.txt | diff -u file.txt -
Of course, this is only practical when working with a single file. Though I'd imagine if one threw in the `--files-with-matches -0` flags together and piped that into `xargs`, something similar to `rep` could be be achieved. (Not that I'd encourage anyone to do this at all)

[0] https://joeyh.name/code/moreutils/


> Not that I'd encourage anyone to do this at all

Why so?


Honestly, upon further thought I think it should be fine as long as the diffs look good.

I just personally would feel more confident with using perl for anything to do with multi-file substitutions. In theory, ripgrep with `--passthru` and `-r` should work.


Writing a correct POSIX script to rename files programmatically in Unix is surprisingly tricky; one reason is symlinks, the other is the binary nature of pathnames (the third one is the anemic POSIX spec, but we make use of what we have). It is good that most people went with Perl for safety.

If I have time, I'll check these utils to see if they handle all pathnames correctly.


Somewhat similar to the approach taken by qmv (from the renameutils package) where file renaming is done by editing the names of the files in a text editor.


Also vidir deserves a mention here, it is a relatively old tool but, combined with speed of navigation that comes with Vim, it has replaced most of my uses of "mv". Wish these tools supported copying files by duplicating lines in editor, I see there is qcp for something similar but I don't see a reason why both functionalities couldn't be combined.


pretty fun

reminds me of emacs wdired mode


To the author: The GIFs on the GitHub pages seem to be about double the speed from the YouTube videos. I find them too fast to follow.


I like it. Generalizing this one step further, there are a bunch of actions like this that are hidden inside our IDEs (whether VSCode / Neovim plugins or built-in functionality), which might be nice to pull out into standard unix tools.

Particularly as we get more investment in AI driving tooling, this sort of lift might be more and more common.


It is great to see that the simplicity of Emacs wgrep inspires new tools. The Emacs interface is more powerful than this new tool because one can more easily do selective or arbitrary edits in the search results, but the new tool is easier to integrate into command line scripts and may be more practical in the end. I like the modality of seeing the diffs until you’re fine with them and then applying them.


This looks pretty neat! I especially like how well it composes with other tools.

Wonder how well it compares with fastmod [0]? That's what I've been using for large scale codemods/refactors. ripgrep is ofc insanely fast so ripgrep+ren would probably fare favorably.

[0]: https://github.com/facebookincubator/fastmod/


If you could pipe the ripgrep output into an editor, make changes, and have them applied, that would be great. Once you get all the text you want to change in one buffer, it should be easy to make repetitive changes quickly in your editor of choice.


I think you can. You pipe to `vim`, and it will open a buffer with the contents. I am not sure if it will pipe out, though.

Edit: I've now tried it myself with `rep`.

It's not automatic, unfortunately. You can use `vipe` (part of moreutils on my machine) to pipe into vim (or any `$EDITOR`) and to pipe the result out.

However, `rep` wants a pager. If you run `vim` with `rep` at the end of the pipeline, everything gets garbled because `rep` (well, the pager) is the one taking key presses.

To get around this, you can pipe `rep` to something else. I piped it to `cat -`, which forced it into outputting to `stdout`.

So my final command looked like this:

    rg --no-heading --with-filename --line-number "\bstatic inline\b" include/ src/ tests/ \
        | vipe | rep inline y_cinline | cat -
Make sure your `$EDITOR` variable is set correctly for `vipe`.

Anyway, I'm going to add a PR to `rep` to add a command-line flag to output to stdout without a pipe. If it is accepted, that should help.


The website breaks for a phone interface, the code lines are neither scrollable nor break lines:/


Yes, I'm somewhat curious what the end of that find command is before it gets piped to xargs. I need to remember to -prune more often.


It's a find command that prunes the .git dir.

Personally, I prefer git grep




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: