Vim: 複数ファイル検索/置換をもうちょい詳しく

/web/tool

Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。

こんちわSig.です。今回は第29回Sugamo.cssで発表したネタ。スライド出せば良いや−、と思っていたのだけど@soh335がピュアな視線で訴えてくるので、重い腰を上げてBlog記事にしてみることにしました。

さて、Vimにおいて多数のファイルを対象に検索→置換する場合、だいたい2つの方法があります。

  • :argdo %s/FooBar/BarBaz/gce | update
  • :bufdo %s/warnings/warningssss/gce | update

順に、引数リストを使う方法、バッファリストを使う方法です。:bufdoの類似で:tabdoとか:windoとかもありますが、まあ使うとすればこの辺でしょう。これらに関しては、過去の記事でも書きました。複数ファイル置換の定番というか王道というか、普通は:argdo使いますよね。ただ、この方法には個人的に以下の問題点があります。

  1. 検索の前に、リストに追加する作業が必要である。
  2. 実行する前に、検索結果一覧を確認できない。
  3. 2.の理由から、数百ファイルが対象となると確認が非常に面倒である。

対象ファイル、ファイル数、検索結果くらいは実行前に確認したいし、確認せずに実行するのは非常にリスキーですよね。そこで以下の方法を取ります。

  1. grepで置換ファイルをリスト化
  2. 置換部分を確認/編集
  3. 納得して一括置換

Vimのgrep検索

vimでgrepを扱うには、ざっくり2種類の方法があります。

内部grep:vimgrep /検索文字/j **/*.txt | cw
外部grep:grep -r 検索文字 *.txt | cw

内部grepというのは、もともとVimに組み込まれているgrepです。特に何もしなくても最初から使用でき、全てのシステムで動作します。Vimで編集可能なら文字コードとか気にせず検索可能ですし、KaoriYaパッチが当たっていれば日本語も日本語パスも問題なし。Vimの検索パターンで使えるので、複数行にマッチする検索パターンも使えます。(まあ厳密に書くのは面倒なんですが)

memo: 内部grepで「**」とすると、デフォルトで30階層までのディレクトリにマッチします。
つまり再帰検索用です。詳しくは:help **

対して外部grepは、UNIX等で使うgrep系ツールを使用した検索です。たとえば以下のような設定をしておいて使います。


    set grepprg=grep\ -nH
    

あるいはフルパスを入れてやる。


    set grepprg=c: /cygwin/bin/grep\ -nH
    

Ackも使えます


    set grepprg=ack\ -a
    

外部grepの性能は、使用するツールに依存します ...が、基本的にvimgrepより速いです。それはもう圧倒的に。内部grepは一度Vimに読み込んでから利用するので、対象ファイルが多くなるほど重くなってしまうんですね。さすがにまともに取り込むとメモリ消費が凄いことになるので、初期設定ではバッファは解放されることになっています。(:h hide)

ちなみに以下のようにすると、:grepからvimgrepが使えるようになります。


    set grepprg=internal
    

あんま利点は思いつかないのだけど... 外部grepを使おうとするpluginは意外とあるっぽいので、:Unite file_mruなんかが便利になるかもしれない???

memo: grep version 2.5からはマルチバイトに対応しているそうです。2.6からは更に劇的に速くなっているとのこと。thx! @songmu

Quickfix

:grep:vimgrepの検索結果はQuickfixウィンドウにリスト化されています。「| cw」は検索終了と同時にQuickfixウィンドウを表示する指定。このQuickfixウィンドウの機能を利用することで、任意の検索結果にジャンプすることができます。

:copenQuickfixウィンドウを開く。
:ccloseQuickfixウィンドウを閉じる。:cclと同じ。
:cnext次の結果へジャンプ。:cnと同じ
:cprevious前の結果へジャンプ。:cpと同じ
:cc1212番目の結果へジャンプ。

次がいよいよ本命です。

Qfreplace

thincaさん作。Quickfixに一覧されたリストから置換バッファを作成する。これを編集/保存することで一斉置換できる。

  1. Quickfix内で「:Qfreplace」とすると編集バッファが作成される。
  2. 適当に編集後、「:w」で一斉置換される。

これがもうメチャメチャ便利! LifeChangeです。ぜひ使ってみてください。

» https://github.com/thinca/vim-qfreplace

VimFiler (unite-grep)

ここまで引っ張った割にはオチがあっさりしすぎのような気もする、ので、ついでにもうひとつ。

Demo: unite-grep & Qfreplace from sigwyg on Vimeo.

Shougoさん作で、VimFilerというプラグインがあります。これはVim上で動作する高機能なファイラーで、ファイルの移動・リネーム・コピペ・削除などなど大体のことはできるというもの。特に「システムの関連付けでファイルを開ける(画像ならプレビューが。PDFならAdobe Readerで表示される)」と聞いて導入したのですが、なんとQfreplaceが実行できるようになりました。

  1. 対象ディレクトリをmark (<Space>)
  2. mark た状態で「gr」とタイプ
  3. 検索パターンを入力→検索
  4. 対象ファイルをmark (<Space>)
  5. <Tab>「a」を押すとアクション選択画面へ
  6. 「replace」を選んでEnter

これでQfreplaceの置換バッファが立ち上がります。正確には「unite-grepからQfreplaceが利用できるようになり、それがVimFilerに導入された」とするべきですが割愛。そこら辺の波瀾万丈はTogetter - 「@sigwyg のVimレベルが増加中です」をどうぞ。いろいろ勉強になりました。Shougoさんリスペクト。

日本のvimmer

最後にもう一つ。今回いろいろな日本発のVim-pluginをそれなりに利用する機会に恵まれたわけですが、その感想はだいたい以下。

  • helpがすごい充実している
  • キャッチアップが凄まじい。ボソッとTweetするだけで反応してくれる。
  • まずGitHubで探すべき。vim.orgよりGitHubのが新しい。
  • プラグインの使い方に関して、ブログにはあんま情報ないかも

日本のVim-plugin制作者の文化には「とにかくHelpを書け!」てのがあるらしく、たとえば「:h Unite」とでもしようものなら凄い量のドキュメントが確認できます。これがとにかく凄くて目を通せば大抵のことは書いてあるのですが、主な利用者が「Helpを読めば解る人種」であるためか、Vim初心者がまず躓く内容—どのように利用するのか、どのへんが便利なのか、このカスタムはどういう仕組みでどういう風に動くのか—が判りにくかったりします。

「○○の設定が良かったのでコピってみた」みたいなのなら見つかるんですけどね。autocmdの使い方も怪しいレベルだとなかなか敷居が高いというか。その辺りの情報も、もっと増えると良いんだけどな−。1行解説みたいな。

ということで書いてみま... お目汚しすいませんでした!

Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。