Vimの引数リストと:argdoの使い方 :argsでファイルを絞り込んで一括処理する
Vimに複数のファイル名を渡して起動すると、その一覧はVimの内部に「引数リスト(argument list)」として保持されます。:argsで内容を確認でき、:argdoを使うとこのリストに入っているファイルだけをまとめて処理できます。開いているものすべてを対象にする:bufdoとは対象範囲の決め方が違うので、その違いも含めて使い方を整理します。
目次(10項目)
| コマンド | これだけ覚える |
|---|---|
:args | 引数リストの内容を表示 |
:args {パターン} | ワイルドカードでリストを作り直す |
:argdo {コマンド} | リスト内の全ファイルに一括実行 |
:argdo {コマンド} | update | 一括実行してから保存まで済ませる |
引数リストとは
vim a.txt b.txtのように複数のファイル名を指定して起動すると、その一覧がそのまま引数リストになります。今どのファイルが入っているかは:argsで確認できます。
:args
[a.txt] b.txt c.txt
今カーソルがある(編集中の)ファイルだけが[]で囲まれて表示されます。この一覧はVim起動時に渡された名前がそのまま入っているだけの仕組みなので、後から:editで別のファイルを開いても引数リストの内容自体は変わりません。開いたことがある全ファイルを保持する「バッファ一覧」とは別物で、バッファ側の記号の読み方はVimのバッファ一覧(:ls)の読み方で解説しています。両者を混同すると、後述する:argdoの対象範囲を勘違いしやすいので、まずここを分けて覚えておくと理解が早くなります。
:argsでリストを表示・作り直す
:argsは表示専用ではなく、後ろにパターンを渡すとリストそのものを作り直せます。ワイルドカードで対象を一括指定できるのが強みです。
:args *.txt
[a.txt] b.txt c.txt
サブディレクトリの中まで再帰的に集めたいときは**を使います。
:args **/*.py
[sub/x.py] sub/y.py
:args {パターン}を実行するたびに、それまでの引数リストの内容は完全に置き換わります。既存のリストに追加されるわけではなく、作り直しである点に注意してください。
:argadd / :argdeleteで追加・削除する
すでにあるリストを保ったまま追加・削除だけしたい場合は:argaddと:argdeleteを使います。
:args a.txt b.txt c.txt
:argadd sub/*.py
[a.txt] sub/x.py sub/y.py b.txt c.txt
追加された2ファイルは末尾ではなく、その時点でカーソルがあったファイル(a.txt)の直後に挿入されています。:argaddは常に末尾に足されるわけではなく、現在位置の直後に挿し込まれる点は見落としやすいところです。削除は名前かパターンで指定します。
:argdelete b.txt
[a.txt] c.txt
ファイル間を移動する
引数リスト内を移動するには:next・:previous・:first・:lastを使います。動きはそのままの意味で、[]の位置が今編集しているファイルを示します。
:args a.txt b.txt c.txt
:next
a.txt [b.txt] c.txt
:last
a.txt b.txt [c.txt]
:previous
a.txt [b.txt] c.txt
:first
[a.txt] b.txt c.txt
リストの端で範囲外に進もうとするとエラーになります。
:last
:next
E165: Cannot go beyond last file
:argdoで一括処理する
引数リストの本当の価値は、リスト内の全ファイルに同じコマンドを一気に流せる:argdoにあります。構文は:argdo {コマンド}で、指定したコマンドが各ファイルに順番に実行されます。
apple
banana
:argdo %s/apple/orange/gorange
banana
この1行で、引数リストに入っている3ファイルすべてに置換が効きます。1ファイルずつ:nextで移動して:sを打ち直す必要はありません。
変更の保存に注意する(hidden / update)
:argdoそのものには保存の機能がなく、しかも次のファイルへ移動する際に直前のファイルへ未保存の変更が残っていると、そこで処理が止まります。
:argdo %s/apple/orange/g
E37: No write since last change (add ! to override)
これを避けるには2つの方法があります。1つは:set hiddenを先に設定しておく方法です。hiddenを有効にすると未保存のバッファのまま次に移れるようになりますが、保存はされないので最終的にはどこかで書き出す必要があります。もう1つは:argdoのコマンドの末尾に| updateを続ける方法で、置換と同時に保存まで済ませられます。updateは変更のあったバッファだけを書き出すので、変更がないファイルまで無駄に上書きすることはありません。
| 方法 | 挙動 |
|---|---|
:set hiddenだけ | 巡回はできるが保存はされない |
:set hidden + | update | 巡回しながら変更のあるファイルだけ保存される |
実際に3ファイルへ:argdo %s/apple/orange/g | updateを実行すると、対象の3ファイルだけが書き換わって保存されることを確認しました。
:argdoと:bufdoの使い分け
:argdoと:bufdoは書き方も動きもよく似ていますが、対象に取る範囲が違います。:bufdoは今開いている全バッファが対象になるのに対し、:argdoは引数リストに明示的に入れたファイルだけが対象になります。開いている範囲全体をまとめて処理するwindo/bufdo/tabdoの使い分けはVimで複数ファイルを一括処理する方法で整理しているので、対象の決め方が違う本記事とあわせて読むと役割分担が見えてきます。
| 観点 | :argdo | :bufdo |
|---|---|---|
| 対象 | 引数リストに入っているファイルだけ | 開いている全バッファ |
| 絞り込み | :args **/*.pyのようなパターンで事前に確定できる | 開いた/閉じた結果に左右され、事後の絞り込みが弱い |
| 向く場面 | 特定の拡張子・ディレクトリだけを狙い撃ちしたいとき | 今のセッションで開いているものすべてに手を加えたいとき |
この違いは動きにはっきり出ます。引数リストをa.txt b.txt c.txtにした状態で:edit d.txtで別ファイルを開き、:argdo %s/apple/orange/g | updateを実行すると、引数リストに入っていないd.txtは書き換わりません。同じ状態で:bufdoを使うと、開いているバッファであるd.txtも対象になります。
d.txtへの影響 | |
|---|---|
:argdo | 引数リスト外なので変わらない |
:bufdo | 開いているバッファなので変わる |
実用例:拡張子で絞り込んだ一括置換
:argdoが最も活躍するのは、特定の拡張子だけを対象にした一括置換です。例えばPythonファイルだけを対象に書き換えたい場合、次のように書けます。
:args **/*.py
:argdo %s/print(/logging.info(/g | update
:bufdoで同じことをしようとすると、対象を絞り込むために必要なファイルだけを先に開いておく手間が発生します。:argdoならパターンで絞り込んだ時点で対象が確定するので、開く手順を挟まずに済みます。置換そのものの書き方は:substituteコマンド活用集で扱っているものと同じなので、複雑なパターンを使いたい場合はそちらも参考にしてください。
設定ファイルが複数のディレクトリに分かれているリポジトリで、拡張子が.confのファイルだけを対象に値を書き換えたいことがありました。バッファを片っ端から開いて:bufdoをかけると、作業中に開いていた無関係なファイルまで巻き込んでしまいます。:args **/*.confで対象を絞ってから:argdoを使うようにしてから、狙ったファイルだけを安全に一括処理できるようになりました。
まとめ
引数リストはVim起動時に渡したファイルの一覧で、:argsのパターン指定によって後から絞り込み直せます。この絞り込みの自由度が:argdoの強みで、開いている範囲全体に効く:bufdoとは対象の決め方がはっきり分かれています。書き換えを保存まで一気に済ませたいときは| updateを付けるか、あらかじめ:set hiddenを設定しておくと安全です。