How I handle bulk text changes in Vim

Published: (February 24, 2026 at 03:35 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Piping Text Through External Commands (!)

Vim’s ! operator sends text through a shell command and replaces it with the command’s output.

  • :%!{cmd} – runs the command on the entire buffer.
  • :5,20!{cmd} – runs it only on lines 5‑20.
  • Visual selection followed by ! automatically uses the range :'.

If the command exits with a non‑zero status or produces no output, Vim restores the original buffer, making it safe to experiment. Undo (u) is still needed if the command crashes part‑way through.

Examples

:%!jq .

Formats a one‑line JSON response pasted from a terminal.

:%!sort -u

Deduplicates a list.

:%!column -t

Aligns a table of values.

:5,20!indent

Formats only lines 5‑20.


Case‑Transform Atoms in Substitutions

Vim’s replacement string supports case‑transform atoms:

AtomEffect
\UUpper‑case everything that follows
\LLower‑case everything that follows
\uUpper‑case the next character only
\lLower‑case the next character only
\EEnd a previous \U/\L transformation

Combined with capture groups, these atoms let you normalize identifiers without external scripts.

Examples

:%s/\v/\u\L\1/g

Upper‑cases the first character and lower‑cases the rest of each word inside “.

:%s/\v(\w+)/\U\1/g

Upper‑cases all environment‑variable names (e.g., SCREAMING_SNAKE, screaming_snake).

:%s/_\(\w\)/\u\1/g

Converts snake_case to SnakeCase by capitalising the character after each underscore.

:%s/\U\1\E-\2/\U\1-\2/g

Upper‑cases only the first capture group while leaving the second unchanged.


Interactive Search Navigation (incsearch)

With incsearch (enabled by default in Vim 8+ and Neovim), and let you jump forward and backward through matches while still in the search prompt, so you can preview the exact location before committing.

Usage

  1. Type /pattern in normal mode.
  2. Press to move to the next match, again for the following one, etc.
  3. Press “ to move backward.
  4. When the desired match is highlighted, press “ to accept it.

Example: To land on the third occurrence of user in a file:

/user

This avoids blind n/N navigation, especially in large files. It works only when incsearch is set (:set incsearch).


Deleting Structured Blocks with the Global Command (:g)

The :g (global) command can operate on ranges, making it ideal for removing marked blocks such as generated HTML scaffolding or log sections.

Syntax

:g/{start_pattern}/,/{end_pattern}/{cmd}

Examples

:g//,//d

Deletes every block between the HTML comments and.

:g/DEBUG START/,/DEBUG END/d

Strips all debug dumps from a crash log.

Preview before deletion

:g//,//p

Prints the lines that would be removed, letting you verify the range.

Notes: :g processes matches top‑to‑bottom and updates line numbers as it goes, so consecutive blocks are handled correctly. If an end marker appears before its start marker, the range may expand unintentionally—always preview with p first.


Refactoring Across a Project with Quickfix (:cdo)

The :cdo command runs a given Ex command at each entry in the quickfix list, providing per‑occurrence precision. This is useful after populating the quickfix list with :vimgrep (or similar) to locate all instances of a pattern across a codebase.

Workflow

  1. Populate quickfix:

    :vimgrep /deprecatedCall/ **/*.go
  2. Apply a substitution at every match and save the file:

    :cdo s/deprecatedCall/replacementCall/ | update
    • | update writes the file after the substitution.
    • Use :cdo norm @q to replay a recorded macro at each location when a regex isn’t sufficient.

Alternatives

  • :cfdo runs the command once per file (useful for file‑wide changes).
  • :cdo includes duplicate entries if the same line appears in multiple search results.

Remember that :cdo does not save files automatically; always append | update (or | w) unless you prefer to review changes first.


Closing Thoughts

These five Vim commands—external piping, case‑transform substitutions, interactive search navigation, global block deletion, and quickfix‑driven refactoring—cover a wide range of bulk‑editing scenarios without requiring any plugins. They work identically in both Vim and Neovim.

For more practical Vim tricks, visit .

What bulk‑editing task in Vim still feels slower than it should?

0 views
Back to Blog

Related posts

Read more »

DevOps and Vibe Coding: A Journey

Things to Do Map Your Application - Map your application on paper, in a spreadsheet, or using graphics/flowcharts. This is the first step. - Understanding the...