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/
\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
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:
| Range | Applies to |
|---|---|
:5,10s/.../.../g | Lines 5 through 10 |
:%s/.../.../g | The whole file — % is shorthand for 1,$ |
:.,+5s/.../.../g | The current line through 5 lines below it |
:'<,'>s/.../.../g | A 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"
: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
#), to confirm the pattern is correct before deleting anything.