Search and Replace
Summary: in this tutorial, you will learn master vim's powerful search, substitute, and global commands to find and replace text across your files with precision.
Search and Replace
Searching is one of the fastest ways to navigate and edit files. Vim's search features go far beyond a simple "Find and Replace" dialog — you can search with patterns, replace text across an entire file in one command, and run commands on every matching line.
What you'll learn in this tutorial:
- How to search forward and backward through a file
- How to move between search results
- How to search and replace text (single occurrence or all at once)
- How to use simple patterns to match flexible text
- How to use the powerful
:globalcommand to act on every matching line - Useful search settings that make your life easier
Basic Searching
Searching Forward
To search for text in Vim, press / (forward slash) from Normal mode, type what you're looking for, and press Enter:
/hello " Search forward for the text "hello"Here's what happens step by step:
- Press
/— a cursor appears at the bottom of the screen - Type the text you want to find (e.g.,
hello) - Press Enter — your cursor jumps to the first match
- If there are multiple matches, keep reading to learn how to cycle through them
Searching Backward
Use ? instead of / to search backward (toward the beginning of the file):
?hello " Search backward for "hello"Moving Between Matches
After you've searched for something, use these keys to jump between matches:
| Key | What It Does |
|---|---|
n | Jump to the next match (in the direction you searched) |
N | Jump to the previous match (opposite direction) |
So if you searched forward with /hello:
nmoves forward to the next "hello"Nmoves backward to the previous "hello"
If you searched backward with ?hello:
nmoves backward (the direction of your search)Nmoves forward
Quick way to search for a word: Put your cursor on any word and press * (asterisk). Vim instantly searches forward for that exact word. Press # to search backward. This is extremely useful for finding all uses of a variable name or function name — just put your cursor on it and press *.
Search Settings (Make Searching Easier)
Add these settings to your ~/.vimrc file to make searching much more pleasant. If you followed the installation tutorial, you may already have some of these:
set incsearch " Show matches as you type (don't wait for Enter)
set hlsearch " Highlight ALL matches in the file
set ignorecase " Searches are case-insensitive by default
set smartcase " ...BUT become case-sensitive if you type a capital letterWhat do these do?
incsearch(incremental search): As you type/hel, Vim immediately jumps to the first match for "hel" — you don't have to press Enter first. This lets you see results live as you type.hlsearch(highlight search): Every match in the file gets highlighted in yellow (or your theme's highlight color). Very helpful for seeing how many matches exist.ignorecase: Searching for/hellowill match "hello", "Hello", "HELLO", etc.smartcase: If you search for/hello(all lowercase), it matches everything. But if you search for/Hello(with a capital), it only matches "Hello" exactly. This gives you the best of both worlds.
Turn off those yellow highlights: After a search, those highlights can be distracting. Type :noh (short for :nohlsearch) and press Enter to clear them. The highlights come back the next time you search. Many people add a keyboard shortcut for this in their vimrc:
nnoremap <leader>h :nohlsearch<CR>This lets you press \h to quickly clear search highlights.
Search and Replace (The Substitute Command)
The substitute command is Vim's equivalent of "Find and Replace." It uses this format:
:s/old/new/Let's break that down:
:— enters Command-line modes— the substitute command/old/— the text you want to find (the "search pattern")/new/— the text you want to replace it with
Replace One Occurrence on the Current Line
:s/hello/world/This finds the first occurrence of "hello" on the current line and replaces it with "world." It only changes one match on one line.
Replace ALL Occurrences on the Current Line
Add the g flag (which stands for global — meaning "all matches"):
:s/hello/world/gNow it replaces every "hello" on the current line, not just the first one.
Replace in the Entire File
Add % before the s to apply the command to every line in the file:
:%s/hello/world/gThis is the most common form. It means: "On every line (%) in the file, substitute (s) every occurrence (g) of hello with world."
Ask for Confirmation Before Each Replace
Add the c flag to get a confirmation prompt for each match:
:%s/hello/world/gcFor each match, Vim will ask: replace with world (y/n/a/q/l/^E/^Y)?
| Option | What It Does |
|---|---|
y | Yes — replace this one |
n | No — skip this one |
a | All — replace this one and all remaining matches |
q | Quit — stop replacing |
l | Last — replace this one, then stop |
The c flag is very useful when you're not sure if every match should be replaced. For example, if you're renaming a variable from count to total, you might not want to change the word "count" inside a comment or string literal. The confirmation prompt lets you decide case by case.
Case-Insensitive Replace
Add the i flag or use \c in yor pattern:
:%s/hello/world/gi " The 'i' flag makes it case-insensitive
:%s/\chello/world/g " \c in the pattern does the same thingBoth of these will match "hello", "Hello", "HELLO", etc.
Substitute Command Cheat Sheet
Here's a summary of all the forms:
| Command | What It Does |
|---|---|
:s/old/new/ | Replace first "old" on current line |
:s/old/new/g | Replace ALL "old" on current line |
:%s/old/new/g | Replace ALL "old" in the entire file |
:%s/old/new/gc | Replace all, but ask for confirmation each time |
:%s/old/new/gi | Replace all, case-insensitive |
:5,10s/old/new/g | Replace all on lines 5 through 10 |
:'<,'>s/old/new/g | Replace all in the visually selected text |
That last one is super handy! If you select some text in Visual mode (V for line selection) and then type :s/old/new/g, Vim automatically fills in '<,'> (which means "the selected range"). So you can easily do find-and-replace on just a portion of your file.
Replacing on Specific Lines
You can specify exactly which lines to search:
:5,10s/old/new/g " Only on lines 5 through 10
:.,+5s/old/new/g " From the current line (.) to 5 lines below
:.,$ s/old/new/g " From the current line to the end of the fileWhat do the symbols mean?
.means "the current line" (where your cursor is)$means "the last line in the file"+5means "5 lines after"5,10means "lines 5 through 10"
Special Characters in Replacements
Some characters have special meaning in the replacement part of the substitute command:
| Character | What It Does |
|---|---|
& | Inserts the entire matched text |
\1, \2 | Inserts the first/second captured group (see patterns below) |
\n | Inserts a newline (line break) |
\t | Inserts a tab character |
\u | Makes the next character uppercase |
\l | Makes the next character lowercase |
\U | Makes all following characters uppercase (until \E) |
\L | Makes all following characters lowercase (until \E) |
Example — changing case:
:%s/hello/\U&/g " Replace "hello" with "HELLO" (\U makes it uppercase, & is the match)
:%s/HELLO/\L&/g " Replace "HELLO" with "hello" (\L makes it lowercase)Simple Patterns (Regular Expressions)
Sometimes you need to search for a pattern rather than exact text. For example, "find any number" or "find any word that starts with get."
Vim supports regular expressions (often called "regex") — a way to describe text patterns. Here are the most useful basics:
| Pattern | What It Matches | Example |
|---|---|---|
. | Any single character | h.t matches "hat", "hot", "hit" |
* | Zero or more of the previous character | ab*c matches "ac", "abc", "abbc" |
\+ | One or more of the previous character | ab\+c matches "abc", "abbc" (not "ac") |
\? | Zero or one of the previous character | colou\?r matches "color" and "colour" |
^ | Beginning of a line | ^# matches lines starting with # |
$ | End of a line | ;$ matches lines ending with ; |
\< | Start of a word | \<get matches "getUser" but not "forget" |
\> | End of a word | ing\> matches "running" but not "ingo" |
[abc] | Any one of these characters | [aeiou] matches any vowel |
[0-9] | Any digit | [0-9]\+ matches one or more digits |
\d | Any digit (shortcut for [0-9]) | \d\+ matches numbers like "42", "100" |
\w | A word character (letter, digit, underscore) | \w\+ matches words |
\s | A whitespace character (space, tab) | \s\+ matches one or more spaces |
Vim's regex is slightly different from other tools! In most programming languages, + means "one or more." In Vim, you need to escape it: \+. Similarly, ? needs to be \?. The characters ., *, ^, $, and [ work without escaping in Vim, but many other special characters need a backslash. If your pattern isn't working, try adding or removing \ before the special character.
Practical Search Pattern Examples
/\d\+ " Find any number (one or more digits)
/\<function\> " Find the exact word "function" (not "dysfunction")
/^import " Find lines that start with "import"
/;$ " Find lines that end with a semicolon
/TODO\|FIXME\|HACK " Find any of these words (\| means "or")Practical Replace Pattern Examples
" Remove trailing whitespace from every line
:%s/\s\+$//g
" Add a semicolon to lines that don't already end with one
:%s/[^;]$/&;/g
" Convert snake_case to camelCase (basic version)
:%s/_\(\w\)/\u\1/gLet's break down that last one:
_\(\w\)— finds an underscore followed by a word character and captures that character in\(\).\u\1— replaces with that captured character, but in uppercase (\u).- So
my_variablebecomesmyVariable.
The Global Command
The :global command (:g) lets you run a command on every line that matches a pattern. It's extremely powerful:
:g/pattern/commandThis means: "Find every line matching pattern, and run command on each of those lines."
Common Uses
:g/TODO/p " Print all lines containing "TODO"
:g/^$/d " Delete all blank lines (^$ matches empty lines)
:g/console.log/d " Delete all lines with "console.log"
:g/^#/d " Delete all comment lines (starting with #)
:g!/pattern/d " Delete all lines that do NOT match the pattern:g! (with exclamation mark) inverts the pattern. :g!/important/d means "delete every line that does NOT contain 'important'." You can also write it as :v/important/d — the v command is the same as g!.
Moving Matching Lines
You can also move or copy matching lines:
:g/TODO/m$ " Move all TODO lines to the end of the file
:g/TODO/t0 " Copy (t = transcript) all TODO lines to the top
:g/function/yank A " Yank all lines with "function" into register aSearching Across Multiple Files
To search for text across multiple files, you can use Vim's :vimgrep command:
:vimgrep /pattern/ **/*.js " Search all .js files recursively
:copen " Open the results listThis opens a "quickfix list" — a window showing all matches. You can:
- Press Enter on a line to jump to that match
- Type
:cnext(or:cn) to go to the next match - Type
:cprev(or:cp) to go to the previous match
For quick project-wide search, most developers use a terminal command like grep or ripgrep (rg) before opening specific files in Vim. But :vimgrep is handy for searching within Vim itself.
Summary
Here's what you've learned:
- Search forward with
/pattern, backward with?pattern - Navigate matches with
n(next) andN(previous) *searches for the word under the cursor- Substitute (find & replace):
:%s/old/new/gfor the whole file - Add
cflag for confirmation::%s/old/new/gc - Range substitution:
:5,10s/old/new/gfor specific lines - Special characters:
.(any char),*(zero or more),^(line start),$(line end) - Word boundaries:
\<word\>matches whole words only - Global command:
:g/pattern/commandruns a command on all matching lines :g/^$/ddeletes blank lines,:g/pattern/ddeletes matching lines- Set
incsearch,hlsearch,ignorecase,smartcasein your vimrc for better searching
Practice: Search and Replace
Create a practice file and try these exercises:
vim search-practice.txtPress i to enter Insert mode, type the following text, then press Escape:
Hello World
hello world
HELLO WORLD
The function getData gets data from the server.
The function getUser gets user info.
TODO: fix this bug
TODO: add error handling
This line has extra spaces in it.
var count = 0;
var name = "John";
var age = 25;
Now try these tasks:
- Search for "hello" with
/hello— how many matches does it find? (Depends on yourignorecasesetting!) - Search for "function" with
/function, then pressnandNto cycle through matches - Replace all "TODO" with "DONE" in the entire file:
:%s/TODO/DONE/g - Replace "var" with "let" only on lines 9-11:
:9,11s/var/let/g - Delete all blank lines:
:g/^$/d - Remove extra spaces (replace multiple spaces with one):
:%s/ \+/ /g - Find lines starting with "The":
/^The - Delete all lines containing "TODO" (or "DONE" if you already replaced them):
:g/DONE/d
Show Solution
" 1. Search for "hello"
/hello
" With ignorecase: matches on lines 1 (Hello), 2 (hello), and 3 (HELLO) = 3 matches
" Without ignorecase: matches only line 2 (hello) = 1 match
" Press n to go to next match, N for previous
" 2. Search and cycle through matches
/function
" Cursor jumps to first "function" on line 4
n " Jumps to "function" on line 5
N " Jumps back to line 4
" 3. Replace TODO with DONE globally
:%s/TODO/DONE/g
" Both TODO lines now say DONE
" 4. Replace var with let on specific lines
:9,11s/var/let/g
" Lines 9-11 now start with "let" instead of "var"
" 5. Delete blank lines
:g/^$/d
" All empty/blank lines are removed
" 6. Remove extra spaces
:%s/ \+/ /g
" "extra spaces" becomes "extra spaces"
" 7. Find lines starting with "The"
/^The
" Cursor jumps to the first line starting with "The"
n " Jumps to the next such line
" 8. Delete lines containing DONE
:g/DONE/d
" The two "DONE:" lines are gone
:q! " Quit without saving when doneWritten by the ShellRAG Team
The ShellRAG editorial team writes practical, beginner-friendly Vim tutorials with tested code examples and real-world use cases. Every article is technically reviewed for accuracy and updated regularly.
Learn more about us →