Solvedripgrep Add support for replace in files

One of the usecases that many people have is a simple search and replace in multiple files.
Usually I achieve this functionality with xargs sed for example with ag -l 'hello' | xargs sed -i "s/hello/bye/g.
Since rg contains the replace flag it will be nice to add support for this feature by replacing in the files directly.

This will be a nice addition after #26

An example on how to do this with sed can be found in the comments here.
#74 (comment)

38 Answers

✔️Accepted Answer

z2oh
429

This is more easily accomplished using sed than I think people realize. sed is quite a beast and is not very user friendly, but simple regex find and replace is easier than one may think. To perform find and replace in a directory (using ripgrep to find files with matches) you can do something akin to the following:

(GNU sed)

rg 'foo' --files-with-matches | xargs sed -i 's/foo/bar/g'

(BSD sed) <-- this includes OSx

rg 'foo' --files-with-matches | xargs sed -i '' 's/foo/bar/g'

(keep in mind here that foo can be a regex, as you would expect for ripgrep)

I'll break down what is happening here:

rg 'foo' looks for matches with foo. By using the --files-with-matches flag, we only print out the filenames for files that have matches, but none of the actual matches themselves. We pipe this output (which is a list of matching filenames) into xargs which essentially splits our list of filenames from standard in into individual command line arguments for the next program we execute. This program happens to be sed. The -i flag to sed tells sed to edit files in place. On BSD sed, we need to use -i '' to specify that we do not want file backups (the empty string indicates a lack of backup extension i.e. no backups). Lastly 's/foo/bar/g indicates we want to perform a substitution, replacing matches of the pattern foo with bar, and that we want to perform this substitution globally, i.e. for all matches in the file.

This method works very well for me, fits in the UNIX philosophy, and avoids using any sort of extra shell script. For people googling "ripgrep search and replace", hopefully this will help you use some basic sed to accomplish your goals!

Other Answers:

Let me say this: IMO, the cumbersomeness of sed is not a reason for rg to support basic replacement. The cumbersomeness of sed is a reason to go out and build a replacement for sed itself. We've already covered the point that some people find sed hard to use. I understand and acknowledge that, but I do not want to get into the business of replacing sed at the point in time. Adding simple inline replacements is going to open the flood gates for features requests like, "sed supports doing X, please add it to ripgrep." I do not want to open those flood gates.

In any case, knowing that rg has already opened the file and can do replacements with --replace I don't fully understand the negative of adding an option to make inline replaces (I might be omitting implementation reasons, probably).

rg is a search tool that will never touch your files. Inline replacements means it becomes a search tool that will sometimes mutate your files. This is a huge change because rg now has to be very careful when it starts mutating files. It's a completely different implementation path than searching and requires being absolutely sure that you don't mess up the end user's data. (What happens if the end user Ctrl-C's in the middle of writing a file?)

I, as the maintainer of this project, do not want to go down this road. I admit that the patch idea is a clever compromise, but I find the the UX to be very bad personally. It also doesn't stop the floodgates from opening to start supporting every nook and cranny of sed itself.

I suggest we drop this and wait for the components of ripgrep to get put into a library so that someone can build a better sed.

Because for me (and lots of other developers) sed is a complete anathema, it's not at all easy to get started with or reason with and it's documentation doesn't help much. I'm also not sure how well it deals with unicode.

Most people seem to open up sublime or atom or their IDE when they want to do a search and replace. For me that says a lot about the dearth of decent tools in the terminal for this.

I agree the UX isn't great but since some kind of review step is likely to be required anyway, it couldn't be that much more succinct. If you know what you want you could of course pipe the output of rg straight into patch.

(by the way, I'm a big fan of rg, sorry to sound like I'm demanding lots highly complex features. The current tool is already great.)

I don't think I'd be willing to do this. It would change rg from a tool that will never modify your files to one that will modify your files. It is also a very complicated feature to get right to make sure you don't really mess up files on disk.

Just use sed.

I made a little bash script for using ripgrep as a command-line find-and-replace tool. It might be terrible. But it works great for my use cases so far. (Note: You need to replace (e with (rg on line 21.)

The idea is to first search and see what matches:

rg something

Then, press the Up arrow and edit in a replacement:

rg something --replace SOMETHING

If it looks good, press the Up arrow again and add a w at the beginning (w for write) (although my script is called pre, not wrg for historical reasons):

wrg something --replace SOMETHING

Voila! The changes should be written to disk.

(I'm not saying that ripgrep should add a feature for "writing the replacements" – I'm just sharing that a couple of lines of bash can be enough :) )

(Edit: macOS support thanks to the next comment.)

More Issues: