Search and Replace Multiple Lines in Multiple Files in Ruby

There are many tools for searching and replacing, but most don't support changing multiple lines in one process.

There are a lot of tools out there for searching and replacing across multiple files. Heck, even the code editor I'm using now -- VS Code -- supports search and replace (and it's pretty quick about it).

But when I recently went to search and replace a multi-line string in all files throughout a directory, I noticed there was a lack of tools to accomplish this. The sed command won't do it and neither will my code editor (at least at the time of writing this).

It turns out this is quite easy to accomplish in ruby with an ad hoc script using the Dir class to identify the files and the File class to read and rewrite the file.

Before we get into the guts of this thing, know that this can be a dangerous path down which to proceed. This particular approach is a destructive action and if you're not using version control and you write an error into your script, you could incorrectly and permanently alter files in a negative way. I highly recommend tracking these files with git so you can protect against mistakes.

Writing the Script

Let's consider an instance where you want to remove a particular string ("Hello World") followed by two newlines (i.e. a multi-line search and replace), but we only want to make this change within ruby files within the current directory and all subdirectories.

Our script could look something like this:

# The string we want to replace.
str = "Hello World\n\n"
# Find all ruby files in this directory and its subdirectories.
Dir.glob('./**/*.rb').each do |file|
# Read the file and store the contents in memory.
content = File.read(file)
# Don't do anything ("next")
next unless content.include?(str)
# (Re)Write the file after removing the targeted string.
File.open(file, 'w+') { |f| f.write(content.remove(str)) }

Running the Script

Where you put this script so it can be run is entirely up to you and the way in which you like to work. In general, there are three solid ways in which you can run a ruby script:

  1. Open an IRB instance, paste the command and then run it.

  2. Put the command in a file, and then run ruby path/to/file.rb where path/to/file.rb is the path to your ruby file.

  3. Make it a rake task.

In general, if it's a one-off task you're only going to run a single time, the IRB console is usually the way to go. That way you don't have to store any code. You write it, run it, and then forget about it.

If it's something you're going to run again, or if you may use something similar to it in the future, it may be worth storing as a standalone script or an executable.

Once you've run the script, go check your files and see that your targeted string has been removed!

Let's Connect

Keep Reading

Access the Site Object within a Jekyll Filter

Filters are the way to make liquid work for you, but sometimes we want more context than we are given when running them.

Aug 21, 2018

Dynamic Attributes on Instances in Ruby

Although it's not usually the right choice, when you want to create individual attribute behavior on instances, Ruby has your back.

May 01, 2018

Dynamic Pages in Middleman using Contentful

How to configure and use the Contentful Middleman gem to generate pages in Middleman driven by a content management system.

May 06, 2018