Vimで複数ファイルを一括処理する方法 windo/bufdo/tabdoの使い分け
開いているファイルすべてに同じ変更を加えたいとき、1枚ずつ手作業でやっていては時間がかかります。:windo・:bufdo・:tabdoを使えば、開いている範囲に対してまとめてコマンドを実行できます。存在は知っていても使いどころが分からない、という人向けにパターンをまとめます。
目次(10項目)
3つの違いは対象範囲だけ
3つとも構文はまったく同じで、対象になる範囲だけが違います。
| コマンド | 対象 |
|---|---|
:windo | 現在のタブ内の全ウィンドウ |
:bufdo | 開いている全バッファ |
:tabdo | 全タブ |
用語の関係はバッファ・ウィンドウ・タブの違いで整理しているので、そちらも参照してください。
よく使うパターン
一括置換と保存
複数ファイルの一括置換が最も使用頻度が高くなります。:bufdoに続けて通常の:sコマンドを書くだけで、開いている全バッファに同時に効きます。下は分割編集していたうちの1ファイルぶんの見え方です。
I use emacs on weekdays. emacs has good extensibility.
:bufdo %s/emacs/vim/gI use vim on weekdays. vim has good extensibility.
未保存のバッファがあると置換結果がうまく反映されないことがあるため、保存まで含めて実行したほうが安全です。updateは変更があったバッファだけを保存し、wにすると変更の有無に関わらず全バッファを保存します。
:bufdo %s/emacs/vim/g | update
表示・比較に使う
行番号の一時表示や、開いている複数ファイルの比較にも使えます。
:windo set number
:windo diffthis
diffthisは分割したウィンドウ同士を比較するための機能なので、意味合い上ウィンドウかバッファに対して使うのが自然で、タブに対して使うことはまずありません。
マクロを全体に適用する
記録したマクロを開いているファイル全部に適用したい場合も同じ書き方で構いません。
:bufdo normal! @x
マクロが内容を書き換える場合は、置換のときと同様に保存まで含めておくと安心です。
:bufdo normal! @x | update
外部コマンドを実行する
シェルコマンドを各ファイルに対して実行することもできます。行数をまとめて数えたいときなどに使います。
:bufdo !wc -l %
強制実行したいとき
未保存の変更があると処理が止まってしまう場合、コマンドの直後に!を付けると保存を無視して強制的に実行できます。ただし変更内容は破棄されるので、意図しないデータ消失に注意してください。
:bufdo! %s/emacs/vim/g
{windo|bufdo|tabdo} {コマンド}で共通なので、覚えるのは対象範囲の違いだけです。書き換え系のコマンドを流すときは| updateを付けて保存まで一気に済ませると安全です。
設定ファイルを分割したリポジトリで、複数ファイルにまたがるリネームを:bufdoで一気に片付けたことがあります。sedでも同じことはできますが、Vimで開いた状態のまま実行結果をその場で確認できるのが地味に便利で、以来ちょっとした一括置換はVim側で済ませることが増えました。
実行後にカーソルが戻らない
これらのコマンドは、対象を順に巡って処理したあと、最後に処理したところで止まります。開始した場所には戻りません。
戻りたいときは、先にどこにいたかを記録しておきます。:windo なら Ctrl-w p、:bufdo なら実行前のバッファ番号を控えておくのが簡単です。
:let b = bufnr('%') | bufdo %s/foo/bar/ge | update
:execute 'buffer' b
1行にまとめて書けるので、よく使う形はユーザー定義コマンドにしておくと打つ量が減ります。
hidden を有効にしておく
:bufdo は次のバッファへ移るたびに、いまのバッファを離れます。変更があるバッファを未保存のまま離れられない設定だと、途中で E37 が出て止まります。
set hidden " 未保存のまま切り替えられるようにする
:bufdo %s/foo/bar/ge | update
Neovimは既定で hidden が有効ですが、Vimは無効です。同じコマンドがVimでだけ止まる、という食い違いはここから来ます。update を付けて都度保存すれば、この設定なしでも通ります。
開いていないファイルも対象にする
これらが扱うのは、すでに開いているものだけです。ディレクトリの中のファイルをまとめて処理したいなら、先に引数リストに入れてから :argdo を使います。
:args **/*.js " 対象を引数リストに入れる
:argdo %s/foo/bar/ge | update
検索結果を対象にするなら :cfdo があります。:vimgrep で当たったファイルだけを処理するので、無関係なファイルを触らずに済みます。使い方は横断検索の記事にまとめています。
まとめ
windo/bufdo/tabdoは対象範囲が違うだけで使い方は共通なので、1つ覚えれば残り2つもすぐ使えます。置換・行番号表示・比較・マクロ適用・外部コマンド実行など、応用範囲の広さが魅力です。