Content-Type: text/plain

Over the years the tool I've turned to for mass search and replace operations has varied. Nothing in particular felt like the right balance between ease of use and power, so I made my own.

But first, let's look at the existing options.

#Your favourite text editor

The easiest option may be the text editor you already know and love, but there's no guarantee good at editing text means good at search and replace, even if it's conveniently at arm's reach.

For example, it might be scoped to the current file by default (vim1), making it awkward to expand the scope to your whole project.

Sublime Text's search and replace is powerful enough but due to its GUI nature, you get zero scripting capabilities.

#sed

sed is ubiquitous, but it's also awkward.

  • It's stream-oriented, not file-oriented.
  • It doesn't support PCRE features and syntax, the regex engine you are likely most familiar with. Instead you get POSIX BRE and ERE, both of which have their quirks, to say the least.
  • The search and replace patterns need to be passed as a single argument, so correctly escaping them is error prone.

#sd

sd is a modern sed alternative which addresses some of the pain points of sed.

  • Uses Rust's regex crate for regular expressions, so you get something a bit closer to PCRE.
  • Passes the search and replace patterns as separate arguments, eliminating one layer of escaping.

It does have a --preview flag, but this is similar to sed in that it shows the end result only. It's still just a display format.

#What's missing?

Composition and preview.

What most tools have in common is that once you've decided on your inputs and execute, it just... does it. Did you make any mistakes? Your files are now clobbered. You'll be glad you kept your repo's working tree clean before you started.

Allow me to introduce you to sr2, a composable command-line search-and-replace tool for developers.

How is it composable? Well, it doesn't actually modify any of your files.

Instead it outputs a patch that would apply the changes, which can then be passed to patch -p0 (or equivalent) to actually do the work. This patch is just a sequence of diffs: a familiar sight to any developer who works with source control systems like git.

find -name '*.go' | sr oldFunc newFunc | patch -p0

(More examples can be found in the README.)

This difference is key. You can see the changes before they are applied. You can redirect the output to a file and manually review and edit the patch before applying it. You can concatenate the output from many different search operations into one patch that can be applied at a later stage. You get it.

There's no reason search and replace can't follow the Unix philosophy too.

Note that sr uses Go's regex library, so you get a similar experience to sd in terms of regex support. Both Rust's regex crate and Go's stdlib decided on guaranteed linear-time matching, so this means the full set of PCRE features is not available. Specifically lookaheads, lookbehinds, backreferences, backtracking and such won't work.

It also uses separate arguments for search and replace patterns, so you only have to deal with one level of escaping.

You can find more information along with usage examples in the repo.

Enjoy!


  1. I know there are ways. 

  2. ... which stands for search-replace. You're right, I am great at naming things!