Vim Tips Wiki
Advertisement
Tip 382 Printable Monobook Previous Next

created 2002 · complexity basic · version 6.0


Often search and replace is needed in multiple files. This tip uses the procedures from run a command in multiple buffers to show how a substitute may be executed multiple times using :argdo (all files in argument list), or :bufdo (all buffers), or :tabdo (all tabs), :windo (all windows in the current tab), or :cdo (all files listed in the quickfix list).

All buffers[]

The following performs a search and replace in all buffers (all those listed with the :ls command):

:bufdo %s/pattern/replace/ge | update
bufdo Apply the following commands to all buffers.
%s Search and replace all lines in the buffer.
pattern Search pattern.
replace Replacement text.
g Change all occurrences in each line (global).
e No error if the pattern is not found.
| Separator between commands.
update Save (write file only if changes were made).

The command above uses :update to save each buffer, if it was changed. That is necessary because, by default, Vim will not switch away from a buffer if it has been changed.

One alternative is to set the 'autowriteall' option so changed buffers are automatically saved when required:

:set autowriteall
:bufdo %s/pattern/replace/ge

Another alternative is to set the 'hidden' option so buffers do not need to be saved, then use :wa to save all changes (only changed buffers are written):

:set hidden
:bufdo %s/pattern/replace/ge
:wa

If you don't wish to save the results of your replacement, but want to review each changed buffer first, you can force the bufdo to continue without saving files with bufdo!:

:bufdo! %s/pattern/replace/ge

All windows[]

If you are not dealing with a lot of files, it can be useful to display each wanted file in its own window, then operate on each window. For example, after opening multiple files with a shell command like gvim *.c, you could choose which files you wanted to operate on like this:

:sball Split screen to show all buffers (one window per buffer).
... Move to a window you do not want to change.
<C-w>c Close the window (press Ctrl-W then c).
<C-w>T<C-PageUp> Or, move the window to a new tab page, then switch back to the original tab.
... Repeat until only buffers you want to change are displayed in the current tab page.
:windo %s/pattern/replace/ge Search and replace in all visible windows.
:wa Save all changes.

All files in a tree[]

Suppose all *.cpp and *.h files in the current directory need to be changed (not subdirectories). One approach is to use the argument list (arglist):

:arg *.cpp All *.cpp files in current directory.
:argadd *.h And all *.h files.
:arg Optional: Display the current arglist.
:argdo %s/pattern/replace/ge | update Search and replace in all files in arglist.

A similar procedure can perform the same operation on all wanted files in the current directory, and in all subdirectories (or in any specified tree of directories):

:arg **/*.cpp All *.cpp files in and below current directory.
:argadd **/*.h And all *.h files.
... As above, use :arg to list files, or :argdo to change.

In the above, a forward slash was used in **/*.cpp. That works on all systems (Unix and Windows). If wanted, a backslash can be used on Windows systems.

Replacing current word[]

A common requirement is to replace the word under the cursor in a number of files. Rather than automating the process, it is best to use Vim's procedures. For example:

:arg *.cpp All *.cpp files in directory.
:argadd *.h And all *.h files.
... Move cursor to word that is to be replaced.
* Search for that exact word.
:argdo %s//replace/ge | update Search and replace in all files in arglist.

In the above substitute command:

  • The search pattern is empty, so the last search is used.
  • Type your replacement text instead of replace. If the text is similar to the current word press Ctrl-R then Ctrl-W to insert that word into the command line, then change it.

Alternatively, you might try the following user command or mapping.

" Search for current word and replace with given text for files in arglist.
function! Replace(bang, replace)
  let flag = 'ge'
  if !a:bang
    let flag .= 'c'
  endif
  let search = '\<' . escape(expand('<cword>'), '/\.*$^~[') . '\>'
  let replace = escape(a:replace, '/\&~')
  execute 'argdo %s/' . search . '/' . replace . '/' . flag
endfunction
command! -nargs=1 -bang Replace :call Replace(<bang>0, <q-args>)
nnoremap <Leader>r :call Replace(0, input('Replace '.expand('<cword>').' with: '))<CR>

For example:

:arg *.c All *.c files in current directory.
:set hidden Allow switching away from a changed buffer without saving.
:set autowriteall Or, use this for automatic saving (instead of :set hidden).
... Move cursor to word that is to be replaced.
:Replace whatever Search and replace in all files in arglist; confirm each change.
:Replace! whatever Same, but do not confirm each change.
:wa Write all changed files (not needed if used :set autowriteall).

Instead of the :Replace command, you could use the mapping. Move the cursor to the word that is to be replaced and press \r (backslash, assuming the default Leader key, then r).

In the function, any special characters in the search word are escaped for generality, although that is unlikely to be needed since a word will not contain special characters. If the cursor is on the word old_text, the search pattern will be \<old_text\> so that only instances of the whole word are found.

Comments[]

Thank you so much. It was very very helpful and saved so much of my time.

Ditto! This is so much easier than using sed.

Advertisement