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 %>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.
...
<% end %>
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/gVim alerts if it didn't find the pattern, to disable the alert just append the flag 'e'.
:%s/old_text/new_text/geFor a complete documentation for the substitute Vim command, use vim help.
:help substituteNow, 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 %>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;
...
<% end %>
/, *: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/*/*Finally, another neat option is adding the flag 'c' which will ask confirmation before each substitution.
:argdo %s/, :expire.*)/)/ge | update
:argdo %s/, :expire.*)/)/gec | update
25 comments:
This is a very nice overview of this technique. Thanks for posting it!
Cool. This is pretty useful, thank you.
Yes, thanks for this. I've been using vim for years. Perhaps I should be embarrassed for not knowing how to do this already, but mostly I'm just happy that I now can. Yay!
Very useful, thanks!
Great! Pretty useful thing
Thanks.
Fantastic - very good description, just saved me hours. Thanks!
I've noticed that you can run any arbitrary shell command with args.
For example, this coughs up all rb files in your current directory:
:args `ls *.rb`
So, if you have a script like this:
#!/bin/bash
if [ -d app ] && [ -d config ] && [ -d script ] && [ -d vendor ]
then
find . -name '*.sql' -prune -o -name '*.csv' -prune -o -path './log' -prune -o -path './vendor' -prune -o -path '*/.svn' -prune -o -type f -print
else
echo "Oops! `pwd` doesn't look like a Rails directory!"
exit 1
fi
... and supposing you call it find-rails and you put it in your path, then you can run:
:args `find-rails`
... and you get your whole project tree for handy search-and-replace. The script excludes vendor, svn, log, plus sql files and csv files (if you have any). Also, the script will choke if you're not currently in a rails directory (if you try to run it, vim will produce the cryptic message "E79: Cannot expand wildcards"). This is by design, since a find in the wrong directory can come up with a colossal number of files.
Run :pwd in vim to see your current directory. Do :Rcd (if you're using vim-rails) or whatever to switch to the appropriate rails directory.
Salt to taste.
Here's a slightly more maintainable version of the find-rails script.
#!/bin/bash
if [ -d app ] && [ -d config ] && [ -d script ] && [ -d vendor ]
then
find . \
-name '*.sql' -prune -o \
-name '*.csv' -prune -o \
-name '*.swp' -prune -o \
-name '*.txt' -prune -o \
-name 'schema.rb' -prune -o \
-path './tmp' -prune -o \
-path './log' -prune -o \
-path './vendor' -prune -o \
-path '*/.svn' -prune \
-o -type f -print
else
echo "Oops! `pwd` doesn't look like a Rails directory!"
exit 1
fi
Thanks for this! Well explained. I've been using vim for over a decade now, but there are many corners and abilities I've never really delved into before. This article came up after a simple google, and now I'm a better vimmer for it. Thanks again!
السلام عليكم
لكم انا سعيد برؤية مدونتك هذه - هي اول مدونة بهذا الشكل الإحترافي التي اجدها مكتوبة بواسطة عربي. كنت ابحث عن معلومة حول "في أي إم" ووجدت مدونتك. اتمنى لك التوفيق
حاتم - اميريكا
Thanks!
Thanks for this post. I've been using Vim for only about a month now but everyday I try to learn something new about it. Today I stumbled upon this post and i'm excited to try it out. Also, thanks for @antipode for the great script.
Cheers!
I was looking for a better S&R method over files. Vim user of 6 years and have never used argsdo... Excellent!
is there a way to revert the argdo changes, if its not possible to apply the inverse regexp to the files?
This is very useful. Thank you very much.
Excellent; thank you. Your post seems like the only place that the args/doarg search and replace technique is expounded on.
Very nice! Thanks for sharing.
Great timeless article, thanks for posting. This article helped me solve a problem today and is likely to save me a lot of time in the future.
Thank you!
Thanks! Another priceless tool in my vim toolbox - :argdo. Oh, what more is to come :)
:bufdo s///
Great example - thank you!
Thanks mate, that was an excellent, clear explanation.
I appreciate it.
Paul
This was a huge help to me today. I had to change a method name in 100s of files and this turned it into a 30 minute job instead of 30 hours.
To Find & Replace Multiple Words in Multiple Word files use a simple tool.
It can replace Headers & Footers along with Fonts, Special Characters, Formatting, Colors etc
http://www.technocomsolutions.com/advancefindreplacehelp.htm
Another useful thing when doing this is to use the :set nomore command to disable the prompt to advance pages when performing the replace on a larger number of files than you have screen lines for.
Post a Comment