Type Less and Save Time with Vim's Global Command!

December 20, 2019

Vim's global (:h :g for more info) command is a useful tool to quickly perform Ex command within the file you're currently editing (some common example of Ex commands are: d (delete), t (copy), s (substitute), m (move), etc). Used properly, it can save you a lot of time typing.

g follows the syntax:

:[range]g[lobal]/{pattern}/[cmd]

Here I will cover basic, slightly more advanced, and advanced use cases. If this is your first time using g, you don't have to follow the advanced one yet, feel free to come back later after getting more experience with it. Learning Vim takes a lot of muscle memories. I am still learning new something new about Vim all the time! 🤓

When to use it?

Anytime you find yourself repeating the same Ex command in one file, you should start thinking, "How can I do this in one stroke with :g?"

Basic use cases

Let's start with basic use cases, going to more sophisticated ones.

Suppose you have a file with many "const" and you need to delete them all:

const hello = "world";
const foo = "bar";
const baz = "qux";
// and many more const...

Run :g/const/d and they are gone!

What just happened?

We gave g a pattern: "const" string. It found all lines (g is line-based) that matches the pattern. It performed d (deletion) to those matches.

g by default will perform p (print) if no command is passed. Try it: :g/const/.

It also accepts ranges. Let's say you want to get rid of all empty lines between lines 10 and 20. You can do this by:

:10,20g/^$/d

Note: ^$ is a regex pattern for blank lines.

Slightly more advanced use cases

Let's move on to more fancy use cases.

Suppose you have the following and want to reverse the line order:

const one = 1;
const two = 2;
const three = 3;
const four = 4;

To do that with :g, you can do:

:global/^/m 0

We'd get:

const four = 4;
const three = 3;
const two = 2;
const one = 1;

Pretty cool, but how did it work?

m 0 moves (see :h :m for details) a line into target line number (line 0). If you want to do it yourself manually, you can go to line 1, run :m 0, then go to next line, run :m 0, then next line, run :m 0, etc. You'll notice the lines are gradually being put on top of the latest one. The difference is, g does it for you in one move. (btw, ^ is a pattern for beginning of line; here it selects all lines in a file).

Pretty cool! 😎

You can combine g with substitute (s):

good, bad, ugly
you're so good it's bad
nothing bad here

If we want to substitute all "bad"s into "nice", but only on the lines that contain "good", we can do:

:g/good/s/bad/nice/g

We'd get:

good, nice, ugly
you're so good it's nice
nothing bad here

Advanced use cases

g can be used with macros. The syntax is :

:g/keyword/norm! @q

Let's say we have a the following:

import lib1
import lib2

const something = 'else';

import lib3
// ...and many more imports

Our tasks are:

  1. wrap all libs with {}
  2. capitalize the l in lib

We can create macro to do our tasks. But we don't want to execute our macro on lines not containing import. Does that mean we have to apply our macros individually on each line we want to do it on?

Well, with g, we can select to apply our macros only to lines containing "import"! Imagine the time saving if you have to do it on 100+ lines!

Assuming we have vim-surround installed (for quick brackets wrap), here's how we can do it!

  • Record a macro to do tasks 1 and 2. With our cursor on "i" in import lib1, run:
qq0w~ysiw{q
// qq = record macro in q register
// 0 = go to beginning of line
// w = jump one word
// ~ = capitalize letter under cursor
// ysiw{ = vim-surround: add {} around lib1
// q = exit recording
  • Undo the changes so our line is back to our original state: import lib1
  • Now execute the macro on only lines containing "import" with :g!
:g/import/norm! @q

And there you have it:

import { Lib1 }
import { Lib2 }

const something = 'else';

import { Lib3 }
// and more...

g also accepts a different range. The syntax is:

:g/pattern1/, /pattern2/ {cmd}

This applies the {cmd} only to "blocks" between pattern1 and pattern2 (inclusive).

A practical use case is, suppose we have the following:

const something = {
   c:  'x',
   b:  'x',
   a:  'x',
   d:  'x',
}

const else = {
   d: 'x',
   c: 'x',
   a: 'x',
   b: 'x',
}

const a = 'hello'
const b = 'foo'
const c = 'bar'

And we want to sort only the keys inside something and else and nothing else.

If you do:

:g/\v\{/+1,/\v}/-1 sort

Everything inside your objects are now sorted and everything else remains unsorted:

const something = {
   a:  'x',
   b:  'x',
   c:  'x',
   d:  'x',
}

const else = {
   a: 'x',
   b: 'x',
   c: 'x',
   d: 'x',
}

const c = 'bar'
const a = 'hello'
const b = 'foo'

Which is exactly what we wanted.

Here's the breakdown: :g/\v\{/+1,/\v}/-1 sort

  • \v - activates vim's very magic
  • \{ - selects { (escape special character)
  • +1 - means apply the range 1 line after the match
  • -1 - apply the range 1 line before the match
  • sort - vim's sorting method

And there you have it! Vim's global command. This is just the surface of what it can do. Have fun experimenting!

What other useful cases do you think you can use it for?

Thanks for reading. I started a twitter account to share daily vim tips. If you're interested, follow @learnvim! 😁

© Copyright 2021 Igor Irianto