Skip to content
0

Filter operations

Vim's shell integration turns your editor into a Unix powerhouse. While many users start with Command-Line mode, understanding the progression from explicit commands to keyboard shortcuts unlocks Vim's true potential. This guide follows the natural learning path: from the familiar :{range}!{filter} to the efficient !{motion}{filter} and its handy shortcut [count]!!{filter}.

The Command-Line Mode

The colon-exclamation (:!) is Vim's interface to your shell. It comes in two flavors with crucial differences:

Type 1: Simple Command Execution

  • :!{cmd}: Execute {cmd} with shell
  • See :h :!
vim
:!ls -la        # Run command, show output
:!make          # Compile, see results
:!git status    # Check repository status

Key behavior: Shows output in a pager, returns to editor.

In this post, we don't discuss this type 1, as it is not much fun to view the outputs of the shell commands inside Vim.

Type 2: Range Filtering with :{range}!{filter}

  • :{range}!{filter} [arg]
  • See :h :range!
vim
:.!date         # Replace current line with date ouput
:%!sort         # Sort entire file
:5,10!fmt       # Format lines 5-10
:.,+3!grep foo  # Filter next 4 lines

Key behavior: Replaces selected text with command output.

We skip type 1 and deep dive on type 2, as it can be extremely useful to call external shell commands to help process the text content from inside Vim.

A filter is a program that accepts text at standard input, changes it in some way, and sends it to standard output.

What "Filter" Means in Vim

Filter in Vim refers to: Sending text through an external program (shell command) and replacing the original text with that program's output.

It's called a "filter" because the external program filters or transforms the input text.

Understanding Ranges

Vim's range syntax gives precise control:

RangeMeaningExample
.Current line:.!date
$Last line:$!cat -n
%Entire file:%!jq .
5,10Lines 5-10:5,10!sort
.,+3Current + next 3:.,+3!column -t
/start/,/end/Pattern to pattern:/^##/,/^##/!fold

Real-World :{range}! Examples

vim
" File-wide operations
:%!python3 -m json.tool          # Pretty-print JSON
:%!clang-format                  # Format C++ code
:%!sed 's/foo/bar/g'             # Global replacement

" Selective processing
:10,20!awk '[print $1, $3]'      # Extract columns
:/TODO/,/DONE/!grep -v "^#"      # Remove comments in section
:'a,'b!sort | uniq               # Sort and deduplicate marked text

The Normal Mode

Apart from :! in the Command-Line, we also have an alternative, the filter operator !, in the Normal mode.

  • !{motion}{filter}: Filter {motion} text lines through the external program {filter}

Filter vs Other Operations

OperationWhat it doesAnalogy
d (delete)Remove textCutting with scissors
c (change)Delete and insertCut and paste new text
y (yank)Copy textPhotocopying
! (filter)Transform via external programSending through a machine

The Bridge: From Command-Line to Normal Mode

Once comfortable with :{range}!, you'll notice a pattern: you often filter text selected by motions. Vim provides a direct path from normal mode.

How ! Works

The ! operator in normal mode follows this pattern:

vim
!{motion/text-object}{shell-command}

When you press ! in Normal mode:

  1. Vim enters operator-pending mode (-- O-PENDING --)
  2. You provide a motion/text object
  3. Vim converts it to :{range}! in command-line
  4. You type the shell command

Example Flow: !ipsort

vim
Normal mode: Press !       → -- O-PENDING --
Type: ip                  → Selects paragraph
Command line appears: :.,.+2!_
Type: sort <Enter>        → Paragraph gets sorted
Status: "3 lines filtered"

Common Motions with !

vim
" Line-based motions
!}fmt                    # To next blank line
!Ggrep pattern           # To end of file
!$tr a-z A-Z             # To end of line

" Text objects
!ip sort                  # Inner paragraph
!i" sed 's/old/new/'      # Inside quotes
!a[ jq .                  # Around brackets (JSON/array)
!at fmt                   # Around tag (HTML/XML)

Why Move from :{range}! to !?

  1. Speed: No need to calculate line numbers
  2. Visual: See what you're selecting
  3. Dynamic: Works with cursor position
  4. Composable: Combine with other Vim commands

The Shortcut !!

The Ultimate Efficiency

If ! is the power move, !! is the special move. It's optimized for the most common case: filtering the current line.

  • !!{filter}: Filter [count] lines through the external program filter

What !! Really Means

!! is shorthand for:

  • First !: Filter operator
  • Second !: Motion meaning "current line"

So !! in Normal mode will bring you into the Command-line mode :.!.

Everyday !! Magic

vim
!!date                     # Replace the current line with timestamp
!!tr 'a-z' 'A-Z'           # Uppercase line
!!bc -l                    # Calculate expression
!!rev                      # Reverse characters
!!fold -w 50               # Fold to 50 columns
!!python3 -c "print(eval('$0'))"  # Evaluate Python

With Counts

vim
2!!sort     # Current + next line
3!!fmt      # Current + next 2 lines
5!!grep foo # Current + next 4 lines

The Visual Mode

  • {Visual}!{filter}: Filter the highlighted lines through the external program {filter}

Similarly, pressing ! in Visual mode will bring you into the Command-Line mode :'<,'>!, you can go ahead to type out the shell command.

The Complete Progression

Stage 1: Command-Line Thinking (Beginner)

vim
" I want to sort lines 15-20"
:15,20!sort

Stage 2: Visual Thinking (Intermediate)

vim
" I want to sort this paragraph"
!ipsort

Stage 3: Positional Thinking (Advanced)

vim
" I want to sort from here to..."
!}sort     # To blank line
!Gsort     # To end of file
!apsort    # This paragraph with spacing

Stage 4: Muscle Memory (Expert)

vim
" Quick line transformations"
!!date     # Timestamp
!!tr A-Z a-z  # Lowercase
!!fmt      # Reformat

Practical Workflow Examples

Example 1: Processing Log Files

Command-line approach:

vim
:1,100!grep ERROR | sort | uniq -c
:101,200!grep WARNING | cut -d' ' -f3-

Normal mode approach:

vim
!Ggrep ERROR | sort | uniq -c     # From cursor to end
!}cut -d' ' -f3-                  # Current section

Example 2: Code Refactoring

Command-line approach:

vim
:%!sed 's/old_function/new_function/g'
:20,40!clang-format

Normal mode approach:

vim
!iWsed 's/old/new/'               # Current word
!aBclang-format                   # Current code block

Example 3: Data Cleaning

Command-line approach:

vim
:%!tr -d '\r'                     # Remove Windows line endings
:.,+50!grep -v "^#"               # Remove comments in next 51 lines

Normal mode approach:

vim
!Gtr -d '\r'                      # From cursor down
!}grep -v "^#"                    # To next blank line

Understanding the Conversion

What Really Happens

When you use ! in normal mode, Vim:

  1. Interprets the motion/text object
  2. Calculates the corresponding line range
  3. Converts to command-line syntax
  4. Presents :{range}! for your command

Conversion Examples

Normal ModeBecomes Command-LineNotes
!ip:.,.+2!Paragraph of 3 lines
!G:.,$!Current to end
!}:.,/^$/!To next blank line
!!:.!Current line
!5j:.,.+5!Next 6 lines

Tips for Each Stage

Mastering :{range}!

  1. Use :set number to see line numbers
  2. Mark lines with ma, mb then use :'a,'b!
  3. Visual select (V) then :! auto-fills range

Transitioning to !

  1. Start with !ip (paragraphs are easy to see)
  2. Use visual mode (V) to confirm selections
  3. Practice common motions: }, G, $

Embracing !!

  1. Create muscle memory for common tasks
  2. Use counts: 3!! for multiple lines
  3. Combine with registers for complex commands

Common Pitfalls and Solutions

Problem: grep deletes non-matching lines

vim
" WRONG: Deletes lines without 'pattern'
:.,+5!grep pattern

" RIGHT: Just highlight
:.,+5g/pattern/#

" Or use visual mode
Vjjj:!grep pattern

Problem: Interactive commands hang

vim
" DON'T:
!!less
!!vim

" DO:
:!less filename  # Run externally
:w!less          # View buffer in less

Problem: Binary file corruption

vim
" Check first:
:set binary?
" If yes, be careful with filters

Advanced Techniques

Chaining Commands

vim
" Command-line style
:%!grep -v "^#" | sort | uniq -c | sort -nr

" Normal mode style
!Ggrep -v "^#" | sort | uniq -c | sort -nr

Using Registers

vim
" Store command in register
:let @f = "fmt -w 72"
!ip<C-r>f  # Insert from register

Conditional Filtering

vim
" Only format if line matches
:if getline('.') =~ 'json'
:  .!python3 -m json.tool
:endif

Cheat Sheet

Quick Reference

vim
COMMAND-LINE MODE:
  :!cmd          Run command, show output
  :%!cmd         Filter entire file
  :5,10!cmd      Filter lines 5-10
  :.,+3!cmd      Filter next 4 lines

NORMAL MODE:
  !ipcmd         Filter paragraph
  !}cmd          Filter to blank line
  !Gcmd          Filter to end of file
  !!cmd          Filter current line
  3!!cmd         Filter 3 lines from cursor

COMMON COMMANDS:
  sort            Sort lines
  fmt             Format text
  grep            Search/filter
  sed             Find/replace
  awk             Pattern processing
  jq              JSON processing
  column          Align columns

Conclusion

The journey from :! to ! to !! mirrors the Vim learning curve:

  1. Start with :{range}!{filter} - Explicit, precise, command-line thinking
  2. Graduate to !{motion}{filter} - Visual, dynamic, normal mode efficiency
  3. Master [count]!!{filter} - Fluid, positional, muscle memory power

Each form has its place:

  • Scripts/configs: Use :{range}!{filter} for clarity
  • Interactive editing: Use !{motion}{filter} for speed
  • Quick transformations: Use [count]!!{filter} for efficiency

Remember: They all ultimately become :{range}!{filter} in Vim's engine. The difference is how you specify the range and how quickly you can express your intent.

Start where you're comfortable, but don't stop there. Each step in the progression makes you faster, more fluid, and more powerful in Vim. The shell is your playground—these commands are the gates.

最近更新