Jérôme Belleman
Home  •  Tools  •  Posts  •  Talks  •  Travels  •  Graphics  •  About Me

Vim: Macros, Substitutions, the Global Command

5 Jun 2016

It's natural to think "substitutions" and "regular expressions" to repeat the same change. There's an even more natural way: recording typed characters.

1 Substitutions

They are carried out with the :substitute (:s for short) command. Say we've got the following text file:

foo
bar
baz
boo

... and we'd like to swap the first 2 characters of each line containing b. We'd be tempted to think substitutions and craft a clever regular expression along the lines of:

:%s/\(b\)\(.\)/\2\1

This is a trivial example, and my solution is only simple because I happen to know that lines with a b in them actually start with a b. So I cheated. This wouldn't have worked if one of the lines had had e.g. obo in it. And if the file had been much longer, I wouldn't have been able to check that there wasn't one. Coming up with a substitution involving a regular expression matching a b anywhere on the line and defining the groups to swap them would require more thinking. And in real life things can become much more contrived still.

2 The Global Command

The :global (:g for short) command can already make life a bit easier. It executes an Ex command on a range of lines matching a pattern. It can for instance be used to delete lines. This deletes all the lines matching b:

:g/b/d

The reverse global command, :vglobal (:v for short) executes an Ex command on a range not matching the pattern. It's probably worth stopping for a minute thinking about what is meant by Ex commands. It's those commands that you type into the command line. The :d Ex command (short for :delete) we use here may not seem particularly familiar. It's just that we don't normally come across this one very often because we just use its Normal mode counterpart invoked by simply typing d . Perhaps a more meaningful example then is a :global command line which uses the :substitute command. So let's come back to our original example of swapping the first two characters of lines with a b in them:

foo
bar
baz
boo
obo

This time, we can craft a much more generic :substitute command such as :s/\(.\)\(.\)/\2\1 without worrying about targeting only lines with a b in them, because we'd leave this to the :global command. This, then, would work beautifully:

:g/b/s/\(.\)\(.\)/\2\1

But there's an even simpler and much more readable approach which involves using the :normal (:norm for short) command, to execute Normal mode commands like they are typed:

:g/b/norm xp

That is, x for deleting the character under the cursor and p for putting it back after the cursor.

3 Recording Typed Characters

The :global command makes it easy, especially when jointly used with the :normal command. But it can get even easier by simply recording typed characters. Type q a to start recording characters into the e.g. a register and just do what you would normally do to find a b, move to the start of the line, swap the two characters and move to the next line: / b Enter 0 x p . End the recording with q . Then repeat as many times as you like by repeatedly typing @ a , which you can make many times simply by typing e.g.  9 9 9 @ a .

Apart from its simplicity of operation, what's nice about this approach is that you can make it as interactive as you like and see the effect it has. The downside however is that it's undoubtedly slower to run than using :substitute or :global, not least because all the operations are drawn. You can improve this by turning off redraws:

:set lazyredraw

4 References