How to replace a string in a dozen old blog posts with one sed terminal command

Sat May 06, 2017 · 4 min read

coding · time-saver

June 1, 2018: This post was previously titled “That time 30 seconds, StackOverflow, and sed saved me 30 minutes” and has since been revised and updated with more examples and a couple new doodles. It’s better now.

I’ve had more than a few usernames, URLs, and Twitter handles over the years. Whether it was changing to something that better reflected my current interests or briefly getting caught up in the “.io” domain craze, there always seemed to be a great reason for an Internet presence refresh. The downside to all this fresh rebranding is that it often means needing to update a lot of links. (If you want to redirect an old blog post URL, check out this article too!)

This week, I launched my new website and changed my Twitter username to match. I was about to spend time manually going through all my old blog posts to find and update the URLs when this very future blog post popped up on my screen like Clippy and shook its pixelated head disapprovingly.

Here’s a worthwhile new habit for you: anytime you find yourself going “Ughhh I have to do that? It’ll take forever!” head on over to DuckDuckGo and search for “terminal command (the thing you’re trying to do)”.

Superhero DuckDuckGo doodle

Superhero Duck is here to save your future hours!

Here’s what I found to save myself a whole bunch of mindless tedium.

Update a string in dozens of blog posts using sed

Meet your new friend sed. This amazingly powerful tool lives in your terminal and is available to be totally underused for things like finding and replacing strings in files. (I seem to have a habit of suggesting ways to totally underuse powerful tools, as in my exploration of how to use cron to create desktop notifications, but I digress.)

Current directory, non-recursive

Non-recursive means sed won’t change files in any subdirectories of the current folder.

Run this command to search all the files in your current directory and replace a given string.

// to replace 'foo' with 'bar'
$ sed -i -- 's/foo/bar/g' *

Here’s what each component of the command does:

-i will change the original, and stands for “in-place.”
s is for substitute, so we can find and replace.
foo is the string we’ll be taking away,
bar is the string we’ll use instead today.
g as in “global” means “all occurrences, please.”
* denotes all file types. (No more rhymes. What a tease.)

You can limit the operation to one file type, such as txt, by using:

sed -i -- 's/foo/bar/g' *.txt

Current directory and subdirectories, recursive

We can supplement sed with find to expand our scope to all the current folder’s subdirectories. This will include any hidden files.

find . -type f -exec sed -i 's/foo/bar/g' {} +

To ignore hidden files (such as .git) you can pass the negation modifier -not -path '*/\.*'.

find . -type f -not -path '*/\.*' -exec sed -i 's/foo/bar/g' {} +

This will exclude any file that has the string /. in its path.

Alternatively, you can limit the operation to file names that end in a certain extension, like Markdown:

find . -type f -name "*.md" -exec sed -i 's/foo/bar/g' {} +

Working with URLs: change the separator

In the case of needing to update a URL, the / separator in your strings will need escaping. It ends up looking like this…

find . -type f -exec sed -i 's/https:\/\/www.oldurl.com\/blog/https:\/\/www.newurl.com\/blog/g' {} +

You can avoid some confusion and mistakes by changing the separator to any non-conflicting character. The character that follows the s will be treated as the separator. In our case, using a , or _ would do. This doesn’t require escaping and is much more readable:

find . -type f -exec sed -i 's_https://www.oldurl.com/blog_https://www.newurl.com/blog_g' {} +

(Maybe) endless possibilities!

There’s a lot more that sed can do. I’ll be adding to this living post as I find more examples that are useful. For now, here are some other use cases that you may find handy.