Discovering Vim Global Command

March 20, 2020

Follow @learnvim for more Vim tips and tricks!

Vim's global command (:h :global) can be used to run Ex command in the buffer, allowing you to make edits instantly. Knowing how to use global command can save you a lot of time from doing repetitive tasks.

If Vim is your text editor, or if you are just checking out Vim, this article will show you things you can do with global command.

I wrote an article on global command in 2019. Between that time and now, I have learned new things (even now, I think I am barely scratching the surface of the command's real power).

Overview

This is the basic overview of g. Feel free to skip ahead if you already know it.

Basic Syntax

According to :h :global, the syntax looks like:

:[range]global/{pattern}/{command}

Let's break it apart, one by one. In this section, I will use the command d for demo (I will cover more commands later). For more info, check out :h :delete

We will also use this test.js throughout this article, so you can code along:

const one = 1;
console.log("one: ", one);

const two = "two";
console.log("two: ", two);

const three = {num: 3};
console.log("three: ", three);

To remove all lines containing "console", we do:

:g/console/d

How does it work?

  1. First, g scans through the range. If not given any, by default it covers the entire buffer ((1,$)). In his case, it finds 3 matches: all lines containing "console".
  2. Second, d is executed against each matching line.

So if you want to delete all lines containing equal sign (=), you can do:

:g/=/d

Inverse match

To effect non-matching lines instead of matching, instead of :g/pattern/{command}, you do either:

  1. :g!/{pattern}/{command}
  2. :v/{pattern}/{command}

If we run :v/console/d, it will delete all lines NOT containing "console".

Range

We can pass it a range. For example, if we do:

:1,5g/console/d

It will find matches to "console" between lines 1 and 5.

Other range variations:

  1. . means current line. If you are on line one, and you want to pass a range from between current line to line 3, you can do :.,3g/console/d. It deletes all lines matching "console" between current line and 3.
  2. $ means end of current buffer/ file. If you do :4,$g/console/d, it deletes all matching lines between lines 4 and end of file.
  3. +n means n lines after. If we are on line 2 and we give :1,+2g/console/d, it will delete all matches between line 1 and 2 lines from current line.
  4. We can make range to go backwards. For example: :4,2g/console/d deletes every matching lines between lines 4 and 2.

Matching and non-matching

We can combine a matching pattern with non-matching pattern, for example:

:g/console/v/two/d

This finds lines that contain "console", but not "two".

Delimiter

You don't have to use / as delimiter. You can use any single byte character that is not alphabetical (for example, you can use @,\,",|, but a-z won't work).

:g@console@d

This is useful if your pattern contains many "/". For example, :g@https://mywebsite.com/stuff@d is easier to type than :g/https:\/\/mywebsite.com\/stuff/d.

Regex

I won't go deep into regex. But global command pattern accepts regular expression. For example, to find match inside double quotes:

:g/"[^"]*"/d

Explanation:

  • " = literal quote
  • [^"]* = any character that is NOT a quote. This is a common pattern in regex.

Default command

We have been using d as our command. But if we don't specify any {cmd}, global command uses (p) by default.

For example:

:g/console

One use case is if you have a list of TODO scattered across the file, you can use :g/TODO to display all TODOs.

Btw, cool fact: because the default command uses p if no {cmd} is given, This makes its syntax to look like:

:g/re/p
  • g = global
  • re = regex pattern
  • p = print, the default command

Did you notice the spelling?

It spells "grep" - the same grep that lives in your command line.

This is not a coincidence. This command originally comes from Ed Editor, one of the first line text editors. And Grep got its name from Ed. Talk about family tree!

List of useful commands

Global command uses many commands from Ex editor. I will cover some of the useful ones.

delete

Again, the syntax for deletion is: :g/re/d.

Here is a useful command:

  • deleting blank lines :g/^$/d
    • /^$/ is regex for blank line (beginning of line, ^, followed by end of line $)
    • Even better, :g/^\s*$/d removes lines with blank spaces.

substitute

Substitution is another big one. If you are a Vim user, you probably have used :s extensively (:h substitute). Here you can combine s with global command.

To replace all "const" with "let", I can do:

:g/const/s/const/let/g

This reads: "find all lines matching "const", then substitute the "const" with "let".

Some might find :g/const/s/const/let/g tedious to type. Why should I type const twice? Luckily, Vim realizes that and you can just do:

:g/const/s//let/g

If you leave the first substitute argument blank, //, substitute will use the global command patten.

move

You can use m to move matches (for more info: :h :move). The syntax is simple:

:g/pattern/m destination

To move all lines containing "console" to the end of file, do:

:g/console/m $

Here's a useful one:

  • :g/^/m 0 reverses the entire buffer. ^ is the beginning of line, so it will match every line in the buffer

put

Put allows you to put text from register x (:h :put). Basic syntax:

:g/pattern/pu {register}

For refresher on register, check out :h registers.

Let's say you have this stored in your register a: // Test comment. To put that line automatically after "console" text, do:

:g/console/pu a

You don't have to always paste from register. You can make put to output your own text with:

:g/console/pu =\"//Test comment\"

This will put the //Test comment after each console. Unfortunately, we have to escape ".

copy

Vim has Copy method (:h :copy). With g command, it works with t. Basic syntax:

:g/pattern/t {address}

It copies all matching pattern to address.

One use case is, if you want to copy all "console" to the end of the buffer:

:g/console/t $

normal

Normal (:h :normal) is not an Ex command. It allows you to execute Vim's normal mode command. Syntax is:

:g/pattern/normal {normal-mode-command}

If you want to comment out all lines with "console". You can use Vim's global + normal with:

:g/console/normal I//

Vim will find all lines containing "console" and perform I// (go to insert mode before first nonblank in the line, add //).

One of the power of Vim's normal command is that it can run anything normal mode can, including macros (:h :q).

If we want to change all const into let with macros, here is how:

First, create macro to change const to let. With cursor on top first line, first character:

qqciwlet<Esc>q

Explanation:

  • qq = start macro on q register
  • ciw = change inner word (deletes "const" and go to insert mode)
  • let<Esc> = type "let", go to normal mode
  • q = stop macro

With macro q ready, target the remaining "const" and execute it on each line:

:g/const/normal @q

sort

You can combine g with sort (:h :sort). Frankly, I find that sort is easier to use directly with visual mode. If you only have one or two things to sort, the easiest way is to go to visual mode, highlight them, and run :sort.

However, I have one use case that I use somewhat often and I want to mention it here.

For the demo, we will have to use a different text this time.

const someArr = [
  "i",
  "g",
  "h",
  "b",
  "f",
  "d",
  "e",
  "c",
  "a",
]

const someMoreArray = [
  "h",
  "b",
  "f",
  "d",
  "e",
  "c",
  "a"
]
// and more

Let's say you have a huge file with hundreds of these arrays. You want the elements on each array sorted, but not the arrays themselves.

We can use global command to target only the elements of each array to perform sort on. Here is the command: :g /\v\[/ +1,/\v\]/-1 sort (I did something similar on my last article, but let me explain again):

This command consists of 3 parts:

:g /\v\[/ +1,/\v\]/-1 sort
  ^--1st ^--2nd      ^--3rd

1st is our pattern. 2nd and 3rd are our command. Let me explain:

Recall our g command syntax:

:g/{pattern}/{command}

Our pattern is:

/\v\[/
  • It uses Vim's very magic (:h magic)
  • \[ finds match for [, the square bracket opening of our array.

Our command is:

+1,/\v\]/-1 sort

This actually consists of two parts:

[range] {cmd}

What I didn't tell you, is that we can also pass an additional range into our command.

Our command range is:

+1,/\v\]/-1

Our command is:

sort

This is what the command does:

  1. Our g command pattern is [, the array opening.
  2. We execute command, sort, against each pattern.
  3. The command has its own range, starting at the line after [ match, ending at a line before ].
  4. It performs sort within that range.

If your mind is baffled by the sort, don't worry. When I first learned this method, I stared at the screen for 10-15 minutes trying to comprehend it, unsuccessfully. Take your time, come back in a few days with fresh mind.

Tips for continuous learning

Learning Vim is a long-term commitment. Here are some tips to get better:

  1. When you are working on repetitive task, is there a better/faster/programmatic way to do it? Chances are , there probably is.
  2. Read Vim :help, search and ask question online.
  3. When you figure it out, at first it will be awkward and slow. Repeat it until you can do it with little/no thinking.

Happy coding!

Shameless disclaimer: if you enjoy this, there's a 98.39% (not scientifically proven) you will also enjoy my Vim Cookbook: Gourmet Vim. Check it out!

© Copyright 2021 Igor Irianto