How do I send a range of lines as stdin to a shell command without modifying the buffer?
Answer
:[range]w !{cmd}
Explanation
The :[range]w !{cmd} command writes a range of lines to the standard input of a shell command, leaving the buffer completely unchanged. This is subtly different from :[range]!{cmd}, which replaces the lines with the command's output — a critical distinction that experienced Vim users often mix up.
How it works
:[range]— any line range:1,5,'<,'>(visual selection),%(whole buffer)w !— the space before!is significant: it means write to stdin rather than filter and replace{cmd}— any shell command that reads from stdin
Example
To count lines in a visual selection without leaving Vim:
:'<,'>w !wc -l
To send selected lines to a clipboard tool on Linux:
:'<,'>w !xclip -selection clipboard
To validate selected JSON:
:'<,'>w !python3 -m json.tool
The buffer remains untouched in all cases.
Tips
- Contrast with
:'<,'>!cmd(now) — that replaces the selection with the command output - Use
%w !cmdto send the whole buffer as stdin - Output from the command appears in a message area but is not inserted into the buffer; use
:[range]r !cmdif you want the output inserted - This is ideal for linting/validating a range, sending to external tools, or quick ad-hoc calculations