When people learn vim it’s pretty common to learn search and replace, typically taught in this form:
:%s/food/ramen noodles, again/g
This will search through the entire file and replace the phrase food with ramen noodles, again. People even go as far as to learn the following variant which asks for confirmation before replacing each instance:
:%s/food/ramen noodles, again/gc
Some people might even know that the g at the end tells vim to replace every instance on a line (omitting the g tells vim to replace only the first occurrence on each line). However, what people often don’t learn is how truly customizable this command is.
#1 Crouching Tiger, Hidden Feature: You can use (almost) any character as a delimiter.
I always thought you had to use / as the delimiter, but it turns out you can use any character (except for space and alphanumeric characters). So we could do the following in place of the above:
- :%s+food+ramen noodles, again+g
- :%s|food|ramen noodles, again|g
Why would we want to use this? Well, if you ever wanted to use / as part of your search or replace expressions, you would have to escape it using vim’s escape character (backslash) . Let’s assume we want to replace each instance of / with ./ To accomplish that, we’d have to do the following if we stuck with the default delimiter:
However, if we used an alternative delimiter, such as @ we would have an expression which is much easier to use, read, and type:
No escaping needed, tada!
#2 That’s Not a Bug, That’s a Feature: You don’t always have to include a search pattern!
Let’s say you previously looked for “a_good_time” using this command:
And after some time you decide you want to replace it with something more specific, you could use the following command:
When you don’t specify a search expression in the substitute command, vim automatically uses the last value you searched (or substituted for) as the search expression!
#3 The Feature Is In Your Hands: You can manipulate the range on which substitutions take place!
The generic version of substitute uses % as the range, which means the entire file, but vim allows much more flexibity in specifying the range. How do we specify ranges? Quite intuitively! Ranges can be specified from a starting point to an ending point, thus the only thing to really learn is to specify these points. They are as follows:
- . the current line number
- $ the last line in the file
- % the entire file
- any number specifies that line number in the file
- ‘t would specify the location of marker t (markers are a way of marking locations in the file so you can return to them later)
- /some_pattern would go to the next line where we find the string “some_pattern”
- ?some_pattern would go to the previous line where we find the string “some_pattern”
You specify ranges as points separated by a comma. Here are some example ranges as used in a substitution command:
- :1,3s/hello/goodbye/g - this would replace all instances of hello with goodbye on lines 1-3 of our file
- :1,$s/hello/goodbye/g - this would search through the entire file Note that 1,$ is equivalent to %
- :1,.s/hello/goodbye/g - this would search from the beginning of our file to the current line
- :.,/aloha/s/hello/goodbye/g - this would search from the current line to the first line after the current line containing aloha in it and replace hello with goodbye
Note: If you specify a range that goes backwards, vim will prompt you to reverse the order.
And there you have it! Now you can be pickier with your substitutions than a vegan on an airplane. Next on your to do list should be learning vim regular expressions, specifically escape characters and quantifiers (for advanced searching) and grouping and backreferences (for advanced replacing).