Advanced Search & Substitution

Vim Intermediate/Advanced

Chapter 4 — Advanced Search & Substitution

Search & Substitute, Beyond the Basics

Learning Vim covered /pattern search and a basic :s/old/new/ substitution. Both go considerably further than that: Vim has its own regex dialect, substitution can target a precise range of lines rather than just "here" or "everywhere," and the global command turns any Ex command — not just substitution — into something applied line-by-line across a pattern match.

Vim's Regex Flavor (a Quick Orientation)

If you've worked through the site's own Learning Regular Expressions course (regex1, built around grep/sed/awk/Bash), be aware: Vim uses its own regex dialect, distinct from POSIX BRE/ERE and from PCRE. By default (Vim's "magic" mode), characters like + and ? are treated as literal characters, not quantifiers — you'd need to escape them as \+ and \? to get the regex meaning.

# default "magic" mode — needs backslashes for + and ?:
:s/colou\?r/color/

# \v "very magic" mode — behaves much closer to PCRE/regex1's conventions:
:s/\vcolou?r/color/
Start every nontrivial pattern with \v. Prefixing a search or substitution with \v switches to "very magic" mode, where most special characters (+, ?, |, (, ), {) work the way they do in the regex flavors regex1 covers, without needing individual backslash-escaping. It's the single easiest way to bring familiar regex intuition into Vim.

Search: New Tricks Beyond /pattern

/\cHello      # \c forces case-insensitive, just for this search, regardless of 'ignorecase'
/\<cat\>     # \< and \> are word-boundary anchors — matches "cat", not "category" or "concat"

gn is a genuinely powerful combination with Chapter 3's text objects: it selects the next match of your last search pattern as a text object in its own right.

/TODO<CR>   # search for TODO
cgnNew Task<Esc>   # change the match just found, then...
.                     # ...repeat with the dot command to change the NEXT match too

cgn followed by repeated . presses is often a faster way to change several matches one at a time (with a chance to skip ones you don't want) than a blanket substitution.

The Substitute Command In Depth

:s/old/new/      # replaces the FIRST match on the CURRENT line only
:s/old/new/g     # the "g" flag — ALL matches on the current line
:s/old/new/gc    # "g" + "c" — confirm each replacement individually (y/n/a/q)
:s/old/new/gi    # "g" + "i" — case-insensitive for this substitution
Without the g flag, only the first match per line is replaced. This is the single most common substitution surprise — running :%s/foo/bar/ across a file where several lines contain foo twice will silently leave the second occurrence on each of those lines untouched. If you want every occurrence, the g flag isn't optional.

Capture groups from the pattern are available in the replacement as \1, \2, and so on — and & refers to the entire match:

:s/\v(\w+)@(\w+)/\2@\1/   # swaps the two halves around an @ symbol
:s/error/[&]/g            # wraps every match in brackets: "error" -> "[error]"

Ranges — Applying Substitution to Multiple Lines

Every Ex command, including :s, accepts a line range immediately before it:

RangeApplies to
:5,10s/.../.../gLines 5 through 10
:%s/.../.../gThe whole file — % is shorthand for 1,$
:.,+5s/.../.../gThe current line through 5 lines below it
:'<,'>s/.../.../gA Visual selection — auto-filled when you press : right after selecting
# select some lines in Visual mode first:
Vjjjj        # select 5 lines
:              # Vim auto-fills:  :'<,'>
s/foo/bar/g  # type just the command — the range is already there

The Global Command :g

:g/pattern/cmd runs any Ex command against every line matching pattern — substitution is only one of many commands it can drive.

:g/pattern/d          # DELETE every line matching pattern
:g!/pattern/d         # delete every line NOT matching (or use :v/pattern/d, identical meaning)
:g/TODO/s/$/ !!/      # append " !!" to the end of every line containing TODO

Combined with Chapter 1's macros, :g becomes a way to apply a whole recorded sequence to every matching line, not just a substitution:

:g/^ERROR/normal @a   # run macro 'a' on every line starting with "ERROR"
Preview a global command before running it destructively. :g/pattern/# lists every matching line with its line number, without changing anything — a safe way to confirm a pattern matches exactly the lines you intend before following up with :g/pattern/d or any other command that actually modifies the file.

A Worked Example

Workflow — cleaning up a config file: strip comments and blank lines

1 :g/^\s*#/# Preview first: list every line that's a comment (starts with optional whitespace then #), to confirm the pattern is correct before deleting anything.
2 :g/^\s*#/d Delete every comment line, now that the pattern's been confirmed.
3 :g/^\s*$/d Delete every remaining blank (or whitespace-only) line, leaving only the actual config values.

Commands Introduced in This Chapter

Chapter 4 — Command Reference

Regex mode
\v ..."Very magic" mode — closer to standard regex, less escaping needed
Search extras
\cForce case-insensitive for this search only
\< \>Word-boundary anchors
gnThe next search match, as a text object — combine with operators
Substitution flags
gAll matches per line, not just the first
cConfirm each replacement individually
iCase-insensitive for this substitution
\1, \2, &Capture group / whole-match references in the replacement
Ranges
:5,10A specific line range
:%The whole file
:'<,'>The last Visual selection
The global command
:g/pattern/cmdRun any Ex command on every line matching pattern
:g!/pattern/cmd (or :v)Run on every line NOT matching
:g/pattern/#Preview matching lines with line numbers before acting