Guides

Vim commands cheat sheet: search, find and replace, delete lines

Vim find and replace with :%s/old/new/g, search with / and n, delete lines with dd, ggdG or :g/pattern/d. A Vim commands cheat sheet with real output.

Published ~10 min read
On this page

Short answer: to find and replace in Vim, press Esc and type :%s/old/new/g, then Enter: % means every line, g every match on a line, and an added c asks before each change. To delete a line, press dd in normal mode; 5dd deletes five lines, and ggdG or :%d deletes everything. Search with /word, then n and N.

Vim, normal mode (press Esc first)
/error            " search forward for "error", n = next, N = previous
:%s/old/new/g     " replace every "old" with "new" in the whole file
:%s/old/new/gc    " the same, but confirm each match
dd                " delete the current line (5dd deletes five)
:g/DEBUG/d        " delete every line that contains DEBUG
ggdG              " delete all lines (:%d does the same)

The results quoted on this page come from runs with vim -es (Vim’s scriptable Ex mode) on Debian 12, Vim 9.0.1378 from the vim and vim-tiny packages, against a 12-line test file in a scratch directory on 21 October 2026. The c prompt, on-screen messages and highlighting follow Vim’s :help. Saving and quitting have their own page: how to exit vim.

Vim commands cheat sheet

Vim has modes. Keys in normal mode are commands, keys in insert mode are text, and : opens the command line at the bottom of the screen. Esc always brings you back to normal mode, so press it whenever you are unsure where you are.

TaskKeys or commandTyped in
Start typing texti (before cursor), a (after), o (new line below)Normal mode
Back to normal modeEscAny mode
Move left, down, up, righth j k lNormal mode (arrow keys work too)
Next word / previous wordw / bNormal mode
Start / end of the line0 / $Normal mode
First / last line of the filegg / GNormal mode
Go to line 4242G or :42Normal mode / command line
Search forward / backward/word and ?wordNormal mode
Next / previous matchn / NNormal mode
Replace in the whole file:%s/old/new/gCommand line
Replace, asking each time:%s/old/new/gcCommand line
Delete the current lineddNormal mode
Delete five lines5ddNormal mode
Delete to the end of the lineD or d$Normal mode
Delete all linesggdG or :%dNormal mode / command line
Delete lines that match:g/pattern/dCommand line
Copy (yank) a line / paste ityy / p (after), P (before)Normal mode
Select characters, lines, a blockv / V / Ctrl-vNormal mode
Undo / redou / Ctrl-rNormal mode
Repeat the last change.Normal mode
Save / save and quit / quit without saving:w, :wq, :q!Command line

Most normal-mode commands take a count in front: 3w jumps three words, 5dd deletes five lines, 3u undoes three changes.

In normal mode, / opens a search at the bottom of the screen. Type the pattern, press Enter, and the cursor jumps to the next match. ? does the same backwards. n repeats the search in the same direction, N in the other one. At the end of the file the search wraps to the top and Vim says search hit BOTTOM, continuing at TOP; a pattern that is nowhere in the file ends with E486: Pattern not found.

CommandWhat it finds
/errorNext "error" after the cursor, case-sensitive by default
?errorPrevious "error" before the cursor
n / NNext match in the same / the opposite direction
*Next occurrence of the whole word under the cursor (# goes backwards)
/error\cIgnore case for this one search (\C forces case)
/\<error\>Whole word only, not "errors" or "myerror"
:set ignorecase smartcaseIgnore case, unless the pattern has a capital letter
:set hlsearchHighlight every match on screen
:nohClear the highlight until the next search (short for :nohlsearch)

Case is where people get caught. The test file had Error, error and ERROR on lines 5, 6 and 7. /error found only line 6, and n stayed there. /error\c visited lines 5, 6 and 7. With ignorecase and smartcase set, /Error matched only line 5 again, because the capital E switches case sensitivity back on. * on DEBUG searched for \<DEBUG\>, the word with boundaries.

Vim find and replace: :s and :%s

Replacing is the :s (substitute) command on the command line:

Vim command line
:[range]s/pattern/replacement/[flags]

Without a range, :s works on the current line only, and without the g flag it changes only the first match on each line. That is why the full “replace all” form is :%s/old/new/g. On a two-line file this is what each variant did:

Command (cursor on line 1)Line 1: foo foo fooLine 2: foo bar
:s/foo/X/X foo foofoo bar
:s/foo/X/gX X Xfoo bar
:%s/foo/X/X foo fooX bar
:%s/foo/X/gX X XX bar

Flags: g, c, i, I, n and e

FlagExampleEffect
g:%s/old/new/gEvery match on a line, not just the first
c:%s/old/new/gcAsk before each change
i:%s/error/WARN/giIgnore case for this pattern
I:%s/error/WARN/gIRespect case even with ignorecase set
n:%s/DEBUG//gnCount matches, change nothing
e:%s/zzz/y/geNo error if nothing matches (useful in scripts and with argdo)

In the test file, :%s/error/WARN/gi turned all three spellings on lines 5 to 7 into WARN; with :set ignorecase active, :%s/error/WARN/gI changed only the lowercase one on line 6. :%s/DEBUG//gn printed 2 matches on 2 lines and left the file alone, which is the quickest way to count something in Vim.

With c, Vim highlights each match and waits for a key. Per :help :s_c: y replaces this match, n skips it, a replaces this one and all remaining ones, l replaces this one and stops, and q or Esc stops without replacing it.

Ranges: lines 10 to 20, a selection, a block

RangeLines it coversExample
%Every line in the file:%s/a/b/g
10,20Lines 10 to 20:10,20s/^/# /
.The current line (the default):.s/a/b/
.,+2The current line and the next two:.,+2d
.,$From the current line to the end:.,$s/a/b/g
'<,'>The last visual selection:'<,'>s/^/# /
/start/,/end/From the next line matching start to the next line matching end, both searched from the cursor:/^Error/,/^ERROR/s/^/# /

You rarely type '<,'> yourself. Select lines with V, press :, and Vim fills it in; then add s/^/# / to comment out exactly the selected lines. In the test, two lines selected with Vj got the # and nothing else did. To limit a replacement to lines that contain some other word, combine it with :g: :g/DEBUG/s/trace/TRACE/ changed trace only on the two DEBUG lines.

Slashes in the pattern: escape them or change the delimiter

Paths are full of slashes, and every one inside the pattern or the replacement needs a backslash. Any other single-byte character except a letter, a digit, \, " or | can be the delimiter instead (per :help pattern-delimiter), and both lines below produced root /srv/www/old;:

Vim command line
:%s/\/var\/www/\/srv\/www/g
:%s#/var/www#/srv/www#g

&, \1 and other special replacements

In the replacement, & stands for the whole match, and \1 to \9 for what the first to ninth \( \) group captured. Two runs against the test file:

Vim command line, with the result
:%s/[0-9]\+/<&>/g
  listen 80;                       ->  listen <80>;
  proxy_pass http://127.0.0.1:8080;  ->  proxy_pass http://<127>.<0>.<0>.<1>:<8080>;

:%s/^\(\w\+\): \(.*\)$/\2 (\1)/
  Error: disk full                 ->  disk full (Error)

The first one is also a warning: % means every line, including lines you were not thinking about, such as the IP address in proxy_pass. Count with the n flag or confirm with c before you run a wide replacement. Backslashes pile up quickly, so Vim offers “very magic” mode: \v at the start of the pattern makes ( ) + ? special without a backslash. :%s/\v^(\w+): (.*)$/\2 (\1)/ gave the same result as the second command above.

  • New line in the replacement: use \r. :s/; /\r/g split a; b; c into three lines. \n matches a line end in the pattern, but in the replacement it inserts a NUL byte (per :help s/\n), not a line break.
  • A literal & or ~: write \& and \~. Unescaped, ~ inserts the previous replacement string.
  • Change case: \u uppercases the next character, \U everything up to \E; \l and \L do the opposite.
  • Reuse the last search: an empty pattern takes the last one. After /www, :%s//srv/g replaced www. Search first, check the highlights, then replace.

Replacing in several files

Vim can loop over files with :argdo. In a folder with three .conf files, this changed port=80 in the two that had it, left port=8080 alone thanks to the $ anchor, and saved each file:

Vim command line
:args *.conf
:argdo %s/port=80$/port=8081/ge | update

The e flag keeps a file without a match from stopping the loop, and update writes only the files that changed. For anything you want to run again from a script or a deploy, use sed (sed -i 's/old/new/g' file) instead, and find the files first with grep -rl. Vim’s regex syntax is close to sed’s basic regular expressions, so most patterns carry over.

Delete lines in Vim: dd, 5dd, dG and :%d

Deleting in normal mode is the operator d followed by a motion: dw deletes to the next word, d$ to the end of the line, dG to the end of the file. Doubling the operator, dd, means the whole line. Every line in this table was run against the test file:

CommandDeletesMode
xThe character under the cursorNormal
dwFrom the cursor to the start of the next wordNormal
D or d$From the cursor to the end of the lineNormal
ddThe current lineNormal
5ddFive lines, starting with the current oneNormal
dGFrom the current line to the end of the fileNormal
dggFrom the current line to the start of the fileNormal
ggdGEverything (go to line 1, then delete to the end)Normal
:%dEverythingCommand line
:8,$dLine 8 to the endCommand line
:g/DEBUG/dEvery line containing DEBUGCommand line
:g!/error/d or :v/error/dEvery line that does not contain errorCommand line
:g/^\s*$/dEmpty lines and lines with only spaces or tabsCommand line
:sort uDuplicate lines (sorts the file and keeps one of each)Command line

“Delete all” has two answers that do the same thing: ggdG from normal mode and :%d from the command line. Both left a 12-line file empty, and u brings the content back as long as you have not quit.

Removing lines that match a pattern with :g

:g/pattern/command runs a command on every line that matches. With d as the command it removes them, and the inverted form keeps only the matches. On the test file:

Vim command line, with the result
:g/DEBUG/d            removed "DEBUG trace one" and "DEBUG trace two"
:v/\cerror/d          kept only "Error: disk full", "error: retry", "ERROR: gave up"
:g/^\s*$/d            removed the empty line and the line with three spaces

:v is the short form of :g!; both gave the same three lines. Adding \c to the pattern made the filter case-insensitive. This is the Vim equivalent of grep -v, except that it edits the file in place, so look at the result before you save.

Copy and paste: yank and put

KeysWhat it does
yyYank (copy) the current line; 3yy yanks three
yw / y$Yank to the next word / to the end of the line
p / PPut (paste) after / before the cursor; a yanked line goes below / above
yypDuplicate the current line
ddpSwap the current line with the one below
"ayy / "apYank into register a, paste from register a
:regShow what every register holds

In the test, 1G3yyGp copied the first three lines to the end of the file, and 1Gddp swapped lines 1 and 2. The system clipboard registers "+ and "* only work in a Vim built with +clipboard; both Debian 12 builds used here report -clipboard in vim --version. Over SSH, select and copy with your terminal instead. If code pasted from the terminal arrives with a growing staircase indent, :set paste before pasting and :set nopaste afterwards stops the auto-indent.

Visual mode: select first, then act

v selects characters, V whole lines and Ctrl-v a rectangular block. Move to extend the selection, then press an operator: d deletes, y yanks, c replaces, > and < indent, ~ toggles case, : opens the command line with '<,'> already filled in. A block selection with I types the same text on every selected line, which is the fast way to comment out a block:

Normal mode
5G         " go to line 5
Ctrl-v     " start a block selection
2j         " extend it two lines down
I# <Esc>   " insert "# " at the start of all three lines

That sequence put # in front of lines 5, 6 and 7 of the test file. The text shows on the first line while you type and appears on the others after Esc.

Undo, redo and repeat

u undoes the last change, Ctrl-r redoes it, and . repeats the last change at the cursor, which pairs well with n: search, change one match, then n. for each further one you want. A whole :%s counts as one change, so a single u after :%s/old/new/g restored the original file in the test. More on undo, and on getting out of the editor cleanly, is in how to exit vim.

.vimrc basics: line numbers and syntax highlighting

Settings typed with :set last until you quit. To keep them, put them in ~/.vimrc, one per line, without the colon:

~/.vimrc
set number                       " line numbers (:set nonumber turns them off)
set ignorecase smartcase         " case-insensitive search unless you type a capital
set hlsearch incsearch           " highlight matches, jump while typing
set expandtab tabstop=4 shiftwidth=4
if has('syntax')
  syntax on                      " colours, where the build supports it
endif

Both Debian builds read this file without errors. The if has('syntax') guard is for vim.tiny, which has no syntax highlighting: a bare syntax on made it exit with an error, and typed on its command line it answered E319: Sorry, the command is not available in this version. Per :help defaults.vim, Vim 8.0 and later load defaults.vim (mouse, incsearch, a scroll margin) only when there is no ~/.vimrc, so creating one switches those defaults off.

vim vs vi on a minimal server

On a server, vi is often not the Vim you know. A Debian install without the full vim package gets vim-tiny, and vi starts it with /etc/vim/vimrc.tiny, which sets compatible to behave like the original vi. Rocky Linux and AlmaLinux ship a small vim-minimal build as vi and the full editor as the vim-enhanced package. What the difference meant in the Debian test:

vi (vim.tiny, Debian 12)vim (vim package, Debian 12)
BuildTiny version without GUIHuge version without GUI
:s, :g, dd, visual modeYesYes
Syntax highlightingNo (-syntax)Yes
Vim script (if, let, functions)No (-eval)Yes
u pressed twiceUndoes, then redoes the same change (vi behaviour)Undoes two changes
System clipboardNoNo (-clipboard)

The undo row is the one that confuses people: after two substitutions, uu in vi mode ended with the second change still in place, because the second u undid the undo. Check what you have with readlink -f "$(command -v vi)" and vi --version | head -n 3. The full editor is sudo apt install vim on Debian and Ubuntu and sudo dnf install vim-enhanced on Rocky Linux and AlmaLinux. The Linux commands cheat sheet has the everyday commands around the editor.

Editing configs on a VPS with Vim

  • Count before you replace. :%s/old//gn shows how many matches a replacement will touch; an unexpected number usually means the pattern also hits a comment or a second server block.
  • Test before you reload. Let the service check the file first: sudo nginx -t or sudo sshd -t. An untested typo in sshd_config can lock you out.
  • Keep a way back. On a VMHeaven VPS, the browser VNC console in the panel stays reachable when SSH is not, and a panel snapshot taken before a larger change restores the whole server.

Frequently asked

How do I find and replace in Vim?

Press Esc, then type :%s/old/new/g and press Enter. % applies it to every line and g to every match on a line; without % only the current line changes. Add c (:%s/old/new/gc) to confirm each replacement, or i to ignore case.

How do I delete a line in Vim?

In normal mode (press Esc first), dd deletes the current line and 5dd deletes five lines from the cursor down. :10,20d deletes lines 10 to 20, and :g/pattern/d deletes every line that contains pattern. Press u to undo.

How do I delete all lines in Vim?

Press Esc, then type ggdG: gg jumps to the first line and dG deletes to the end of the file. :%d on the command line does the same. Save with :w to keep the empty file, or press u to bring the text back.

How do I search in Vim?

In normal mode, type /word and press Enter to jump to the next match; n goes to the next one and N to the previous one. ?word searches backwards, * searches for the word under the cursor, and :set ignorecase smartcase makes searches case-insensitive unless you type a capital letter.

Related articles

All guides