Wednesday, July 13, 2011

Search-and-replace in multiple files with vim

Last time we talked about doing a search-and-replace on multiple files using find and sed. That technique is very useful, but sometimes it's more convenient to do it right from within vim.

Vim is my personal favorite text editor. I use it for almost everything, especially as my primary development environment. It did take some time to learn the commands to get around, and it can do so much that I'm still learning things every day, but once I got used to it I found it difficult and tedious to use anything else. The rest of this post assumes you know basically how to get around in vim.

Let's say you wanted to change everywhere in a while that says "Joe" to say "Frank" instead. With regex in vim that's easy:
:%s/Joe/Frank/g

The % says to change every line, the /g says to change every occurance on the line, easy peasy. But what if you want to change several files?

Vim has a lot of powerful scripting you can do, I've barely scratched the surface myself. For what we're doing here we don't need anything complicated, it can all be done on one line (which means you can remember it and use it anywhere).

Here is the whole command:
:args **/* | argdo %s/Joe/Frank/ge | update

I think most of that is pretty clear, but I'll explain what each part does.

args creates a list of arguments. In this case it gets a list of files, if you only wanted php files you would use **/*.php.
argdo tells vim to execute the next statement for every argument that is passed to it. In this case it's regex, but it can be any vim command.
%s/Joe/Frank/ge
You'll notice we've added an e at the end of the regex, that suppresses errors like "No Match" when a file doesn't have our string to replace in it.
update writes the changes, but unlike the command write it only writes to the file if there are changes.

Vim opens a buffer for every file it changes, so you can then step through them to verify the change if you want. (:bn and :bp, next and previous buffer, :bw to close a buffer)

That's it! Simple eh?


Here is one way I used it at work. I had to convert a PHP site that used the template engine FastTemplate to make it use Smarty instead. I wrote an adapter class to make our code work with Smarty, but the template syntax is different also. Both packages have you embed tags for dynamic content in your templates, but FastTemplate tags look like {CONTENT} while Smarty tags look like {$CONTENT}. That's the main difference and it's something I can easily change with regex, but I had close to 100 template files I had to change, which would have taken forever doing it one at a time even with regex.

First I made sure I was in the template directory:
:cd templates

(Okay that wasn't really what I did first, but I wish it had been :), it was first after I recovered...)

Then the command I used looked something like this:
:args **/*.tpl | argdo :%s/{\([A-Z_0-9]\+\)}/{$\1}/ge | update

Find all the .tpl files, then replace any curly brackets with caps, an underscore, or numbers in them with a smarty equivalent. I still had to find any templates that had javascript and add {literal} tags and things that that Smarty requires, but the bulk of the work was done. Since vim left the buffers open I just stepped through each one to make the remaining changes.


So there you go! This is a good example of why I started this blog. I thought for sure someone on the internet would have posted a way to easily convert all their FastTemplate templates to Smarty but I couldn't find anything on Google. I hope this helps someone who runs into the same issue, or one like it.

2 comments:

  1. Nice one.

    Still in transition from Sublime Text 2, which has a Vintage mode that emulates part of VIMs functionality.
    Search and replace across multiple files is one of the only things, where ST2 makes it much easier for the user.

    ReplyDelete
  2. There are many editors that do some things easier than vim, I admit, but to get that convenience you have to give up some of what makes vim powerful. I haven't used any other editor that can so so much (Emacs? Maybe, too long since I used it.)

    But really if you're a professional developer you probably spend more time hanging out with your editor than you do with your wife, so use the one that makes you happy.

    ReplyDelete