2010-01-21 98 views
32

我發現了Vim的替代命令如何在Vim中的目錄中遞歸搜索和替換?

:%s/replaceme/replacement/gi 

而且使用vimgrep ...

:vimgrep /findme/gj project/**/*.rb 

有沒有辦法給他們爲了做所有的文件替換下結合目錄?

+0

[Here](http:// stackoverflow。com/a/13944042/834176)是遞歸搜索和替換的一種方式,但僅限於版本控制下的文件(如果您確實想要搜索目錄下的所有文件,可以很容易地進行推廣)。 – 2012-12-19 01:06:51

回答

47

儘管我是Vim用戶,但我通常使用findsed來處理這類事情。 sed的正則表達式語法與Vim(它們都是ed的後代)類似,儘管不完全相同。使用GNU sed,您還可以使用-i進行就地編輯(通常sed會將修改後的版本發送到stdout)。

例如:

find proj -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} + 

一塊一塊:

  • 在 「凸出」 樹proj =搜索
  • -name '*.rb' =東西的名字匹配 '* .RB'
  • -type f =並且是常規文件(不是目錄,管道,符號鏈接等)。)
  • -exec sed =運行sed這些論點:
    • -i =與就地編輯
    • -e 's/regex/replacement/g' =執行 「替換」 命令(幾乎相同的Vim的:s,但注意不足% - 它在sed默認)標誌的
    • -- =結束,文件名從這裏開始
    • {} =這就告訴find是TH它發現Ë文件名應放在sed中的命令行這裏
    • + =這就告訴find-exec動作完成,我們要組參數成少數sed調用越好(一般比一次運行更高效每個文件名)。要爲每個文件名運行一次,您可以使用\;而不是+

這與GNU sedfind一般的解決方案。在特殊情況下可以縮短一點。例如,如果你知道你的名字模式不匹配任何目錄,你可以省略-type f。如果您知道所有文件都不以-開頭,則可以省略--Here's an answer I posted to another question with more details on passing filenames found with find to other commands.

+6

確保並在給予'sed'之前測試你的'find' - 如果它過於包容,這可能非常糟糕。給'sed''-c'('--copy')選項也是一個好主意,這將防止它與只讀文件混淆。 – Cascabel 2010-01-21 23:06:17

+0

@BroSlow夠公平的。我已經更新了答案,以便更安全/更一般。 (也就是說,在Linux上,至少ARG_MAX是相當大的,對於問題中描述的用例,使用空格或特殊字符的文件名會非常奇怪。) – 2014-11-10 00:49:47

+0

如果使用此方法,則不能使用該構造,因爲sed不支持它,你可以使用'vim -c'%/ regex/replacement/gc'-c'wq' - {} \;'這樣你就可以確認你的改變了 – Funonly 2015-04-23 09:22:58

4

有一個插件正在努力做到這一點:Vimgrep Replace

哦,等等......還有其他的:Global Replace,EasyGrep

對於一個沒有插件的解決方案,或許argdo將有助於如果你能得到vimgrep的輸出到參數列表(可以用args設置),但我似乎無法弄清楚的細節。如果有人採納了這個想法並對其進行改進,我會很高興......所以在這裏。

第一個插件(我猜別人也是......)的基本思想是首先使用vimgrep,然後在:cnext之間循環匹配,並在每行上應用替換命令。完成此功能的函數可能足夠小,可以放入.vimrc中。 (也許你可以舉一個插件的來源?)

(我想hgimenez可能是一個解決方案,但不管它是否合適可能取決於要處理的文件數量......插件應該沒問題。)

+0

謝謝,但arrrrgh!有沒有辦法做到這一點,而無需安裝另一個#!*&%插件? – Ethan 2010-01-21 21:48:33

+0

@Ethan:我感覺你。 :-)我認爲必須有一種方法涉及:args,:argdo,:copen或其他......我只是不使用vimgrep足夠以前對它有過多的考慮,似乎無法將它們放在一起對你來說完美的解決方案。但我仍在嘗試,所以也許我會想出一些東西並進行編輯。 – 2010-01-21 21:52:53

+0

噢,好吧......我似乎無法找到一個乾淨簡單的無插件解決方案,或者確定地告訴我自己沒有任何插件。我的Vim-fu讓我失望了! :-(插件的確看起來不錯,但也許它們值得一試...... – 2010-01-21 22:32:30

4

一種方法是打開所有要運行替換(或任何命令)的文件,然後運行:bufdo <cmd>,它將在所有打開的緩衝區上運行<cmd>

7

您可以運行使用vimgrep然後錄製去在使用vimgrep輸出每一行,並沒有替換,像這樣一個宏:(!不要鍵入#評論)

: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 
[email protected]       #repeat the macro 10000 times, or until stopped by 
           #an "error" such as reaching the end of the list 
:set noautowrite 

我沒有測試過它,所以它可能需要一點調整 - 在一些你不介意首先搞砸的文件上測試它。

這個優於sed的優點是Vim正則表達式更加強大,您可以添加c選項來:驗證每個替換項。

編輯: 我修改了原來的帖子,因爲它是錯誤的。我正在使用:1vimgrep來查找每個文件中的第一個匹配項,但我誤讀了文檔 - 它僅在所有文件中找到一個匹配項。

+0

很酷!在尋找單一的表達解決方案時,我從來沒有停下來考慮一個宏觀...讓我現在感覺有點傻。 +1。 :-) – 2010-01-23 14:36:58

+0

你剛剛救了我幾個小時的工作,謝謝! :D我需要將xml文件中的值複製到文件的另一部分,然後小寫 - 超過200個文件。 – Jason 2012-05-04 15:32:30

45

args and argdo應該做你需要的,例如,

:args spec/javascripts/**/*.* 
:argdo %s/foo/bar/g 

請參閱this page

+0

謝謝!這是非常酷:) – starikovs 2015-04-20 06:03:58

+1

Whoa vimskill ++ – 2017-02-02 18:17:31

+1

年的苦難和質疑職業選擇今天結束 – 2017-04-27 05:26:11

0

這適用於少量文件。

vim`find。 -type f`

:bufdo%s/pattern/replacement/gec |更新