During some refactoring, I was hit with the need to do some serious bad-man search and replace across entire directories from within vim, because almost any alternative would be more painful than listening to Cher’s discography and watching The View simultaneously. After scouring Google results for a while, the only article I could find detailing this technique was a pretty good one by Ibrahim Ahmed.
I’ll summarize with an example. Let’s say you have a massive Python project, and
you want to replace every call to certain accessor functions with a variable
name. For example, wat.getZappaAlbum()
becomes wat.zappaAlbum
. Let’s
say that your Python project is in the ~/Code/albums
directory.
From within vim, we first have to specify which files we want to operate on.
:args ~/Code/albums/**/*.py
That is, we specify args
as any Python file within any subdirectory of
~/Code/albums
. You can use regular expressions here and, in fact, a few
comments on Ibrahim’s site allude to piping in file names with a bash script.
After we have specified the files we want to act on, we specify the action.
Let’s say we have two functions we want to replace: getTreasureFingersAlbum
and getToroAlbum
. We want to change them to treasureFingersAlbum
and
toroAlbum
, respectively.
:argdo %s/get\(TreasureFingers\|Toro\)Album()/\l\1Album/gec | update
Now you’re whipping through each matching instance, yaying or naying each replacement like a glutton prince.
Wait, what? Let’s break down what’s going on here.
:argdo
, unsurprisingly, specifies that we’re acting on the args we
specified earlier.%s/foo/bar/gc
replaces each instance of foo
with bar
and prompts you
before doing so.get\(TreasureFingers\|Toro\)Album()
matches either
getTreasureFingersAlbum()
or getToroAlbum()
and the escaped
parenthesis “capture” either
TreasureFingers
or Toro
for later use in the replacement.\l\1Album
is the meat. The prefix, \l
, tells vim to make the first
character of the following back-reference lowercase. The next component,
\1
, is a back-reference to the capture (either TreasureFingers
or
Toro
), and “Album” is simply appended to the replacement.gec
is used instead of gc
because the inclusion of e
avoids error
messages in the case that we search a file and don’t find a match (this
will happen).| update
simply saves the file after we’ve completed the first
operation: the find and replace.More information about case changes within regular expressions can be found here.