How do I run a search and replace across multiple files?
Answer
:cfdo %s/old/new/g | update
Explanation
The :cfdo %s/old/new/g | update command performs a search and replace across every file in the quickfix list and saves each one. This is the standard Vim workflow for project-wide find-and-replace without any plugins.
How it works
:cfdoexecutes a command once for each file in the quickfix list%s/old/new/gruns the substitution on every line of the current file| updatesaves the file only if it was modified (like:wbut smarter)
Step-by-step workflow
- Populate the quickfix list with matching files using
:vimgrepor:grep:
:vimgrep /oldFunction/g **/*.js
-
Review the matches with
:copento see the quickfix window. -
Run the replacement across all files:
:cfdo %s/oldFunction/newFunction/g | update
Every JavaScript file containing oldFunction now has it replaced with newFunction, and each modified file is saved automatically.
cfdo vs cdo
:cfdoruns the command once per file in the quickfix list — use this for substitutions:cdoruns the command once per entry (match) in the quickfix list — use this for targeted, per-match operations
Handling special characters
When your search or replacement string contains special characters like /, use \V (very nomagic) mode for literal matching and escape forward slashes:
:cfdo %s/\V\Ctext\/html/application\/json/g | update
\Vtreats all characters literally (except\)\Cforces case-sensitive matching\/escapes the forward slash within the pattern
Tips
- Always review the quickfix list with
:copenbefore running:cfdoto make sure you are targeting the right files - Use
:cfdo %s/old/new/gc | updatewith thecflag to confirm each replacement interactively - Use
:vimgrepto populate the quickfix list from within Vim, or:grepto use an external tool likergorag - Omit
| updateif you want to review changes before saving — then use:cfdo updateseparately to save all modified buffers - This requires Vim 8+ or Neovim — older versions do not have
:cfdo - Use
:bufdo %s/old/new/g | updateas an alternative if you already have the target files open as buffers