Reputation: 103
I have 10 files, each containing 'apple' and 'red'. I want to change 'apple' to 'pear' and change 'red' to 'green' across all 10 files.
My current method is typing out this find and replace command :%s/apple/pear/g
(and the same for red to green) in each file. How do I save these two substitutions and have them apply to the 10 files. More generally, how do I do this more efficiently?
I'm new to vim and am not sure whether to look into command history or vim register or something else.
Upvotes: 2
Views: 2659
Reputation: 11916
The quickfix-reflector allows you to edit results in the quickfix and apply those edits back to the original files.
:grep
as usual to search your project for matches (:grep apple -R .
) -- or whatever method you want to populate the quickfix.:%s/apple/pear/ge | %s/red/green/ge
):w
)Normally, the quickfix isn't 'modifiable', but quickfix-reflector will modify that setting and add a hook to propagate changes on save. There are several alternative plugins offering similar functionality and it's a big change to your workflow!
Upvotes: 2
Reputation: 45117
Vim does not come with a project-wide find & replace. It comes down to doing the following steps:
You can run multiple commands one after another with the |
.
Example
:s/apple/pear/ | s/red/green/
Since you updating these files it might be best to also :write
or :update
the file as well
:s/apple/pear/ | s/red/green/ | update
Substitution flags:
g
for "global" or doing multiple substitutions per linee
to suppress errors. We need this as a file may have apple
but no red
for example:s
takes a range like 1,3
to work on lines 1 through line 3. $
represents the last line number. An entire file is 1,$
. Use %
as shorthand for 1,$
:%s/apple/pear/ge | %s/red/green/ge | update
Thoughts:
\<
& \>
around your patterns. E.g. red
pattern as red
can be in different words like predicate
If you started Vim with the list of files you wish to work on then these files are already in the Argument List
$ vim file*.txt
You can supply arguments after vim is started via :args
or :argadd
. e.g. :args file*.txt
Use :args
to see the argument list
Use :argdo {cmd}
to run your command, {cmd}
over all files in the argument list
Often you want to search for a pattern in a group of files and then do a replacement on those matches. Vim's :vimgrep
& :grep
search for files and put these positions into the Quickfix List
:vimgrep /apple\|red/ **/*.txt
You can see the quickfix list with :copen
or :clist
.
Use :cdo {cmd}
/:cfdo {cmd}
to run a command, {cmd}
, over every position/file in the quickfix list
:cfdo %s/apple/pear/ge | %s/red/green/ge | update
:cdo s/apple/pear/e | s/apple/pear/e | update
:grep
:grep
will use 'grepprg'
& 'grepformat'
to execute and read the output of program like grep
(used by default on linux). This is often faster than :vimgrep
at the expense of using a different regex syntax.
Instead of using grep
as your 'grepprg'
, you can use something like ripgrep or ag the silver searcher which are both often even faster than plain grep
.
Example of config options for ripgrep:
set grepprg=rg\ --vimgrep
set grepformat=%f:%l:%c:%m
Now you can do :grep 'apple\|red'
to populate the quickfix list
You can even take this further if you want by running :grep
in a subshell
Vim has other lists like: buffer, window, tabs, location list. These have an associated "do" command with them: :bufdo
, :windo
, :tabdo
, :ldo
:Subvert
Although, no plugins are needed to do such substitutions across files, it might be handy to know about Tim Pope's vim-abolish plugin. Especially if you have many of these substitutions and/or have casing issues. Abolish's :Subvert
/:S
command can reduce your substitution commands to 1 command
:%S/{apple,red}/{pear,green}/gw
This will convert apple
-> pear
and red
-> green
due to their positions inside of the curly braces.
Use :Subvert
's w
flag to only substitute words. Subvert also will handle cases
Here are some Vimcasts episodes which are related:
:cdo
/:cfdo
are in Vim now)Search with :vimgrep
and run your substitutions with :cfdo
:vimgrep /apple\/red/ *.txt
:cfdo %s/apple/pear/ge | %s/red/green/ge | update
Read more using Vim's help system
:h argument-list
:h :argdo
:h :args
:h quickfix.txt
:h :cdo
:h :cfdo
:h :vimgrep
:h :grep
:h 'grepprg'
:h 'grepformat'
Upvotes: 11
Reputation: 8898
You can use a command such as :argdo
or :bufdo
to repeat the same Ex command on many files. (See :help :argdo
.)
For example, you can open your 10 files by passing them as arguments to Vim. If they're all *.txt
files, you can use:
$ vim *.txt
Then you can use :argdo
to repeat the operation on all the files Vim got as arguments:
:argdo %s/apple/pear/eg | update
Of notice here, I'm adding a | update
at the end of the operation, in order to save the file after performing the substitution, for each of the files. That's important, otherwise :argdo
will error out before moving to the next file. (See :help :update
, it's similar to :w
but only saves when there are modifications. The |
is a command separator for Ex commands in Vim.)
An alternative to using | update
is to use :set hidden
, which allows you to start editing another file before moving on to the next one. (See :help 'hidden'
.) You can later use :wall
to write all files once you're done with the modifications everywhere.
Finally, I'm passing an extra /e
flag to the substitution, to prevent it from generating an error if there is no match to replace. (See :help :s_e
.)
You can also chain multiple commands (such as substitution commands) by using |
as a separator. For example:
:set hidden
:argdo %s/apple/pear/eg | %s/red/green/eg
:wall
Upvotes: 2