How do I run a shell command on a range of lines in normal mode and replace them with the output?
Answer
!{motion}{cmd}<CR>
Explanation
In normal mode, !{motion} sends the lines covered by the motion to a shell command's stdin and replaces them with stdout. Combined with !!{cmd}<CR> (which targets just the current line), this lets you apply any Unix filter to any portion of your buffer as a direct text transformation—no visual selection required.
How it works
!{motion}{command}<CR>
!triggers the filter operator — Vim prompts for a shell command{motion}defines the range (e.g.,ipfor inner paragraph,Gfor to end of file,5jfor 5 lines down){command}is any shell program that reads stdin and writes stdout<CR>confirms and runs the filter
!!{command}<CR> is the shorthand that filters only the current line.
Example
Sort a list in place. Place the cursor inside an unordered list and run:
!ipsort<CR>
Before:
banana
apple
cherry
After:
apple
banana
cherry
Or right-align a column of numbers with awk on the current line:
!!awk '{printf "%10s\n", $0}'<CR>
Tips
!Gsort<CR>sorts everything from the cursor to end of file!ipcolumn -t<CR>aligns tabular data in the inner paragraph- Chain filters with pipes:
!ipsort | uniq<CR> - The entire filtered region is one undoable action — press
uto restore the original - For visual selection filtering use
!{cmd}<CR>after making a visual selection; Vim pre-fills:'<,'>!