DaleSchool

Command-line Deep Dive

Beginner15min

Learning Objectives

  • Run external commands with :!{cmd}
  • Insert command output with :read !{cmd}
  • Apply a Normal-mode command to many lines with :norm
  • Run pattern-driven commands with :g/pattern/command

Working Code

You can run shell commands without leaving Vim:

:!ls                 ← list files in the current directory
:!python3 %          ← run the current file with Python (% = current file)
:!node %             ← run the current file with Node.js

You can also insert a command's output into the file:

:read !date          ← insert current date/time below the cursor
:read !ls            ← insert file listing below the cursor

And run a command across multiple lines at once:

:5,10d               ← delete lines 5-10
:5,10s/foo/bar/g     ← replace foo with bar on lines 5-10
:'<,'>norm I//       ← prepend // to each line in the visual selection

Try It Yourself

Suppose you have this code. Open it in Vim and try the commands:

// TODO: implement login
const name = "dale";
const age = 30;

// TODO: add error handling
function greet() {
  console.log("hello");
}

// TODO: write tests
function add(a, b) {
  return a + b;
}

Run external commands:

:!wc -l %            ← count lines in the current file
:!grep TODO %        ← find lines containing TODO

Work with ranges:

:1,3d                ← delete the first three lines
:%s/TODO/FIXME/g     ← replace TODO with FIXME across the file

Practice :norm — apply to many lines at once:

After selecting lines with Visual mode, try:

Select lines with V → :norm I//   ← prepend // as a comment
Select lines with V → :norm 0xx   ← remove the leading // comment

Practice :g (global):

:g/TODO/d            ← delete every line containing TODO
:g/^$/d              ← delete every blank line
:v/function/d        ← delete every line that does NOT contain function (:v is the inverse of :g)

"Why?"

The real power of command-line mode lies in range + command combinations.

:! is the bridge to the terminal. You can run code, check results, and even insert the output — all without leaving the editor. % is a special symbol meaning "the current file name."

Range specifications let you precisely target where the command applies:

RangeMeaning
5,10Lines 5-10
%Entire file
'<,'>Current Visual selection
.Current line
$Last line
.,$From current line to end of file

:norm runs a Normal-mode command across many lines. :norm I// means "on each line, run I// in Normal mode." You get repetitive editing without recording a macro.

:g/pattern/command is Vim's secret weapon. It runs a command on every line matching a pattern. In fact, the name grep itself comes from this command — global regular expression print.

Deep Dive

:g command cookbook
:g/console.log/d          ← delete every console.log line
:g/^#/norm A.             ← append a period to every Markdown heading
:g/^$/d                   ← delete all blank lines
:g/import/t$              ← copy every import line to the end of file
:v/return/d               ← delete every line without return
:g/TODO/norm 0i[x]        ← prepend [x] to every TODO line

The inverse of :g is :v (inVerse). :v/pattern/d deletes lines that do not match.

Sorting lines with :sort
:sort                     ← alphabetical sort of the whole file
:5,10sort                 ← sort only lines 5-10
:sort!                    ← reverse sort
:sort u                   ← sort and remove duplicates
:sort n                   ← numeric sort

Perfect for sorting CSS properties alphabetically or tidying up import statements.

Create a file with the content below and clean it up in Vim:

banana
apple

cherry

date
apple
elderberry

banana
  1. :g/^$/d to remove every blank line.
  2. :sort u to sort and deduplicate.
  3. :%norm I- to prepend a Markdown list marker to every line.
  4. :read !date to append the current date.

Target result:

- apple
- banana
- cherry
- date
- elderberry
(current date)

question: How do you run the current file with Python from inside Vim? answers:

question: What does :g/TODO/d do? answers:

question: How do you prepend // to every line in a Visual selection? answers: