I found out about Vim's substitute command...
:%s/replaceme/replacement/gi
And vimgrep...
:vimgrep /findme/gj project/**/*.rb
Is there a way t开发者_运维百科o combine them in order to do a replacement across all the files under a directory?
args
and argdo
should do what you need, e.g.
:args spec/javascripts/**/*.*
:argdo %s/foo/bar/g
See this page.
Even though I'm a Vim user, I generally use find
and sed
for this sort of thing. The regex syntax for sed
is similar to Vim's (they're both descendants of ed
), though not identical. With GNU sed you can also use -i
for in-place edits (normally sed
emits the modified version to stdout).
For example:
find project -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} +
Piece by piece:
project
= search in "project" tree-name '*.rb'
= for things whose names match '*.rb'-type f
= and are regular files (not directories, pipes, symlinks, etc.)-exec sed
= runsed
with these arguments:-i
= with in-place edits-e 's/regex/replacement/g'
= execute a "substitute" command (which is almost identical to Vim's:s
, but note lack of%
-- it's the default insed
)--
= end of flags, filenames start here{}
= this tellsfind
that the filenames it found should be placed on sed's command-line here+
= this tellsfind
that the-exec
action is finished, and we want to group the arguments into as fewsed
invocations as possible (generally more efficient than running it once per filename). To run it once per filename you can use\;
instead of+
.
This is the general solution with GNU sed
and find
. This can be shortened a bit in special cases. For example, if you know that your name pattern will not match any directories you can leave out -type f
. If you know that none of your files start with a -
you can leave out --
. Here's an answer I posted to another question with more details on passing filenames found with find
to other commands.
You could run vimgrep then record a macro that goes to each line in the vimgrep output and does the replace, something like this: (dont type in the # comments!)
:set autowrite #automatically save the file when we change buffers
:vimgrep /pattern/ ./**/files
qa #start recording macro in register a
:s/pattern/replace/g #replace on the current line
:cnext #go to next matching line
q #stop recording
10000@a #repeat the macro 10000 times, or until stopped by
#an "error" such as reaching the end of the list
:set noautowrite
I have not tested it, so it may need a little tweaking - test it on some files that you dont mind getting messed up first.
The advantage of this over sed is that Vim regex are more powerful, and you can add the c option to :s to verify each replacement.
Edit: I modified my original post since it was wrong. I was using :1vimgrep to find the first match in each file, but I misread the docs - it only finds one match across all files.
To answer the original question of combining vimgrep and substitute:
:vimgrep /pattern/ ./**/files # results go to quickfix list
:set autowrite # auto-save when changing buffers
:silent! cdo %s/replaceme/replacement/gic # cdo draws filenames from quickfix list
:set noautowrite
Leave out the c
of gic
if you don't want to confirm each replacement (in which case you could also use find
and sed
as suggested by @LaurenceGonsalves).
One way would be to open all the files you want to run the substitution on (or any command), and run :bufdo <cmd>
, which will run <cmd>
on all open buffers.
There's a plugin which endeavours to do just that: Vimgrep Replace.
Oh, wait... there are others: Global Replace, EasyGrep.
For a no plugin solution, perhaps argdo
would help if you could get vimgrep
's output onto the argument list (which can be set with args
), but I can't seem to figure out the details. I'd be happy if somebody took the idea and improved upon it... so here it is.
The basic idea behind the first plugin (I'd guess the others too...) is to use vimgrep
first, then cycle through the matches with :cnext
and apply the substitution command on each line. A function to accomplish this could be small enough to put it in .vimrc. (Maybe you can lift one from the plugins' sources?)
(I suppose hgimenez might be on to a solution, but whether or not it's appropriate would probably depend on the number of files to be processed... The plugins should be ok regardless.)
This works for a small number of files.
vim ` find . -type f `
:bufdo %s/pattern/replacement/gec | update
精彩评论