Two and a half years ago, I wrote my first Go program. I wanted to learn another language, and Go looked like a ton of fun: straight forward, easy to learn, and a static binary with no runtime shenanigans. I picked a project and I started hacking. Looking back, the code I wrote is a little cringy, but not terrible. I’d surely do things differently these days, now that I have more Go experience. But we all start somewhere.
However, the tool that I wrote, a recursive search/replace tool which I intelligently dubbed re, is actually incredibly useful: to my own surprise, I use it every day. I haven’t made a single modification to it in all that time (until today for this post). And since I’m in the sharing mood today, I thought I’d share it with the millions of people (cough) that come here every day. Ha!
Why is there no recursive sed out there?
It’s a little odd, isn’t it? When you search for recursive search replace linux in your favorite search engine, you get all sorts of find | xarg sed-type solutions (like here), or find -exec sed ... (like here). And yes, of course, that’s the Linux way: combine the tools that you already have to do what you want. Still, searching and replacing seems like a pretty basic thing to me.
So anyway, for some reason, there is no standard Linux utility that can do it out of the box — something where you can just do re oldvalue newvalue to replace and old value with a new value in all files in the current folder (and all its subfolders).
Introducing the fabulous re
Well you’re in luck. That’s what re is. A tool that can recursively replace strings in one or many directories. By default, it will walk the entire directory tree and replace the search string in all files recursively. You can provide include/exclude wildcards for the filename, e.g. to only replace strings in “*.java” or “.sh” files.
Here’s the basic syntax:
1 2 3 4 5 6 7 |
Syntax: re [options] SEARCH REPLACEMENT [DIR ...] Options: -e string Comma-separated list of excluded files, wildcards supported (default ".bzr,CVS,.git,.hg,.svn") -f Apply changes -i string Comma-separated list of included files, wildcards supported, e.g "*.js,*.html,*index.*" |
And here are a few examples of how I used it in the past (straight from my history):
1 2 3 4 5 6 7 8 9 |
# Shows files that would have been replaced. Without the -f flag, # nothing is actually replaced. $ re CacheDir ClipboardDir # Replace CacheDir with ClipboardDir recursively $ re -f CacheDir ClipboardDir # Replace "Foo(Bar)" with "Foo(Baz)" recursively in all scala files $ re -f -i "*.scala" "Foo(Bar)" "Foo(Baz)" |
Here’s what the output will look like. In this example, I’m not passing the -f flag, so no change will be applied:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ re -i "*.go" var const No changes will be applied unless -f is given. Skipping excluded directory .git + LEGACY/client.go + LEGACY/udp_client.go + LEGACY/udp_server.go + build/lib/_obj/_cgo_gotypes.go + client_conn.go + client_forward.go + cmd/natter/main.go + internal/natter.pb.go + protocol.go 9 file(s) WOULD have been updated. |
You get the idea. It’s simple. That’s it.
If you like it, you can get it from the Github page. Either build it yourself (go build) or check out the releases page.
A. About this post
This post is super short because it’s just a Code Snippet. You can find other short, code-focused posts like this in that section.
Recent Comments