How to run vim command on multiple files

http://www.ibrahim-ahmed.com/2008/01/find-and-replace-in-multiple-files-in.html

Vim: Find and replace text across files

Programmers almost always need the ability to find and replace certain text across multiple files in their projects. Most IDEs provide find and replace functionality. And there are many free tools onlineto just find and replace text across multiple files. Because I am using Vim as my favorite editor for over 15 years now, it came natural to choose Vim for such job, and not depend on another tool.

For a real life example, in one Rails project, I used caching in my view templates. I used a plugin to set an expiration time for the cache. I needed the cache function to look like:

<% cache(“sidebox_latest_news”, :expire => 15.minutes.from_now) do %>

<% end %>

I used this technique in about 25 of my templates. Then i decided not to use timed expiration of the cache. So I needed to remove the expire argument of the cache method. I needed to remove it from all of my 25 templates. I used Vim to find the needed piece of text to be removed.

In Vim, when we need to replace text in a file, we use the substitute command.

:s/old_text/new_text/

This should replace the first occurrence of “old_text” with “new_text” for the current cursor line. To substitute all matching occurrences in the line, just add the flag ‘g’ which refers to global. And to substitute all the file occurrences of the pattern, add the ‘%’ before the substitute command.

:%s/old_text/new_text/g

Vim alerts if it didn’t find the pattern, to disable the alert just append the flag ‘e’.

:%s/old_text/new_text/ge

For a complete documentation for the substitute Vim command, use vim help.

:help substitute

Now, after we knew how to substitute text in Vim, we need to substitute the expire argument. we cannot do without using regular expressions. That’s because the argument is not the same in all the files, it carries different time values, and may differ in spacing.

<% cache(“sidebox_related_reports”, :expire => 1.hour.from_now) do %>

<% end %>

So we need to match the whole argument from the comma at the start till the right parentheses and replace it with empty text. the regular expression we should use to match the argument is;

/, *:expire.+)/

I will quickly explain the pattern above, but please refer to any of the many tutorials online about Regular expression for more details. The pattern starts with a comma which is the start of the text we need to remove. Followed by a space and a ‘*’ which means a comma followed by zero or more spaces then ‘:expire’ followed by “one or more” “character”. And finally the pattern ended with a right parentheses. So our pattern starts with a comma and ends with a right parentheses. Any text matching this pattern in all templates files should be replaced with empty text, or to be specific, with a right parentheses, as the parentheses is already part of the text to be removed.


To apply Vim command to all the templates files, I will use the ‘args’ command. I will pass all the templates files in the app/views folder to ‘args’ using wildcards, and because Rails store the templates files grouped in folders named after their controllers I will use double wildcard ‘app/views/*/*’ to list the files 2 levels deep under app/views folder.

After I passed the required files to ‘args’, I can apply whatever command I like to all these files using the command ‘argdo’. First I will apply the substitute ‘s’ command and then ‘update’ which will only save the modified files.

:args app/views/*/*
:argdo %s/, :expire.*)/)/ge | update

Finally, another neat option is adding the flag ‘c’ which will ask confirmation before each substitution.

:argdo %s/, :expire.*)/)/gec | update