web-dev-qa-db-ja.com

Vimのディレクトリで再帰的に検索および置換するにはどうすればよいですか?

Vimの代替コマンドについて知りました...

:%s/replaceme/replacement/gi

そしてvimgrep ...

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

ディレクトリ下のすべてのファイルを置き換えるためにそれらを組み合わせる方法はありますか?

40
Ethan

私はVimユーザーですが、通常、findsedを使用してこの種のことを行います。 sedの正規表現構文はVimの構文に似ています(どちらもedの子孫です)が、同一ではありません。 GNU sedを使用すると、インプレース編集に-iを使用することもできます(通常、sedは変更されたバージョンをstdoutに出力します)。

例えば:

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

少しずつ:

  • project =「プロジェクト」ツリーを検索
  • -name '*.rb' =名前が「* .rb」に一致するもの
  • -type f =および通常のファイル(ディレクトリ、パイプ、シンボリックリンクなどではない)
  • -exec sed = sedを次の引数で実行します:
    • -i =インプレース編集あり
    • -e 's/regex/replacement/g' = "substitute"コマンドを実行します(Vimの:sとほぼ同じですが、%がないことに注意してください-sedのデフォルトです)
    • -- =フラグの終わり、ファイル名はここから始まります
    • {} =これはfindに、見つかったファイル名をsedのコマンドラインに配置する必要があることを伝えます
    • + =これはfind-execアクションが終了したことを伝え、引数をできるだけ少ないsed呼び出しにグループ化したい(一般に実行よりも効率的)ファイル名ごとに1回)。ファイル名ごとに1回実行するには、\;の代わりに+を使用できます。

これは、GNU sedおよびfindを使用した一般的な解決策です。これは、特別な場合に少し短縮できます。たとえば、名前パターンが-type fを省略できるディレクトリとは一致しません。-で始まるファイルがないことがわかっている場合は、--を省略できます。 これが私が投稿した回答ですfindで検出されたファイル名を他のコマンドに渡す方法の詳細を含む別の質問へ。

66

argsargdoは、必要なことを実行する必要があります。

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

このページ を参照してください。

63
meleyal

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

私はそれをテストしていません、それでそれは少し微調整を必要とするかもしれません-あなたが最初に台無しにされても構わないいくつかのファイルでそれをテストしてください。

Sedに対するこれの利点は、Vimの正規表現の方が強力であり、cオプションを:sに追加して、各置換を検証できることです。

編集:間違っていたので元の投稿を変更しました。私は:1vimgrepを使用して各ファイルの最初の一致を見つけましたが、ドキュメントを誤って読みました-すべてのファイルで1つの一致しか見つけられません。

8
Dave Kirby

1つの方法は、置換を実行するすべてのファイル(または任意のコマンド)を開き、:bufdo <cmd>を実行することです。これにより、開いているすべてのバッファーで<cmd>が実行されます。

6
hgmnz

それだけを行うように努めるプラグインがあります: Vimgrep Replace

ああ、待って...他にもあります: Global ReplaceEasyGrep

プラグインなしのソリューションの場合、argdoの出力を引数リスト(vimgrepで設定できます)に取得できれば、おそらくargsが役立つでしょうが、私には思えません詳細を把握する。誰かがアイデアを取り入れて改善してくれたら幸いです...だからここにあります。

最初のプラグインの背後にある基本的な考え方(他のプラグインもそうだと思います...)は、最初にvimgrepを使用し、次に:cnextとし、各行に置換コマンドを適用します。これを実行するための関数は、.vimrcに配置するのに十分小さい可能性があります。 (多分あなたはプラグインのソースから一つを持ち上げることができますか?)

(私はhgimenezが解決策にあるかもしれないと思いますが、それが適切であるかどうかはおそらく処理されるファイルの数に依存します...プラグインは関係なく大丈夫でなければなりません。)

4
Michał Marczyk

Vimgrepと置換を組み合わせるという元の質問に答えるには:

: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

各置換を確認したくない場合は、c of gicを省略します(この場合、findsedを使用して、 @LaurenceGonsalves)。

3
proton1

これは、少数のファイルで機能します。

vim `find。 -type f `

:bufdo%s /パターン/置換/ gec |更新

0
rado