:globalコマンド活用集 行削除・抽出・normalとの組み合わせ
ログの中からエラー行だけを抜き出したいとき、コメントだけの行をまとめて消したいとき、複数の行に同じキー操作を繰り返したいとき。こうした「行単位の一括処理」を、ノーマルモードの操作を何度も繰り返したりマクロを組んだりせずに片付けられるのが:global(省略形:g)コマンドです。パターンにマッチした行を探し、その行でExコマンドを実行する、という単純な仕組みですが、組み合わせ次第で編集の幅がかなり広がります。
目次(8項目)
| コマンド | これだけ覚える |
|---|---|
:g/pat/d | マッチした行を削除 |
:v/pat/d | マッチしない行を削除(抽出に使える) |
:g/pat/normal! {キー} | マッチした行にキー操作を一括適用 |
:g/pat/m$ / :g/pat/t$ | マッチした行を末尾へ移動 / コピー |
基本形:パターンにマッチした行にコマンドを実行する
:[range]g[lobal]/{pattern}/{command}
{pattern}にマッチした行それぞれに対して{command}を実行します。「パターンで検索して、見つかった行でExコマンドを打つ」という操作を、対象となる全行に自動で繰り返す仕組みだと考えると分かりやすいです。rangeを省略すると、:sでは現在行だけが対象になりますが、:gではファイル全体が対象になります。カーソル位置に関係なく全行が走査されるので、うっかり広い範囲を変更してしまう事故につながりやすい点は覚えておく価値があります。
globalはgまで短縮して書けます。以降はこの記事でも:gと表記します。
マッチした行を削除する(:g/pattern/d)
:gの使い方の中でもっとも出番が多いのが行削除です。
:g/TODO/d
apple TODO fix this banana TODO another cherry
:g/TODO/dapple banana cherry
TODOを含む行がその場で消えます。デバッグ用に仕込んだconsole.logをまとめて消す、コメントだけの行を除く、といった作業はこの1行で終わります。空行を削りたいときはパターンを行頭と行末が隙間なく続く^$にします。
:g/^$/d " 空行だけを削除
マッチしない行だけを残す(:v/pattern/d)
削除の逆、つまり「マッチしない行を消す」こともできます。:gには否定形の:vが用意されていて、:g!と書いても完全に同じ意味になります。
:v/ERROR/d
:g!/ERROR/d " :vと完全に同じ意味
ログファイルからERRORを含む行だけを抜き出したいとき、パターンを反転させて書き直すより:vに切り替えるほうが速いです。TODOを含む行だけを残したい場合は次のようになります。
:v/TODO/d
" apple / banana / cherry が消え、TODOを含む行だけが残る
「マッチしない行を消す」と「マッチした行を残す」は結果として同じことなので、抽出したいパターンさえ思いつけば:vのほうが素直に書けます。
ノーマルモードのキー操作を一括適用する(:g/pattern/normal!)
ここまでのdは:gが呼び出すExコマンドの一例にすぎません。:gの後ろには任意のExコマンドを置けるので、normal!を使えばノーマルモードのキー操作もそのまま流し込めます。:sでは書き換えにくい編集を、覚えているキー操作そのままで行単位に適用できるところが:gの本当の価値です。
:g/{pattern}/normal! {キー操作}
normalではなく!付きのnormal!を使う点に注意してください。!を付けると、ユーザーが定義したマッピングを無視してVim本来のキー操作として実行されます。:gと組み合わせるときにマッピングが割り込んで意図しない動きになる事故を避けられます。
let a = 1 let b = 2; let c = 3
:g/[^;]$/normal! A;let a = 1; let b = 2; let c = 3;
行末が;で終わっていない行だけを探してA;を実行しています。[^;]$は「行末の1文字が;ではない」という意味なので、すでに;が付いている行はマッチせずそのまま残ります。:sで同じ結果を得るなら:%s/[^;]$/&;/のようにマッチした文字を消さずに戻す書き方が必要ですが、normal!ならカーソル移動と挿入をそのまま書けるので発想を切り替える必要がありません。
記録したマクロを流し込むこともできます。
:g/{pattern}/normal @q " レジスタqのマクロをマッチした行に適用
複数手順にわたる複雑な編集をマクロとして組んでから、マッチした行だけに一括適用する、という流れは:sには代替できません。qqで記録を始めて1行分の編集を仕上げ、qで記録を止めたら、:g/pattern/normal @qで対象行にまとめて再生します。
最初は:gを単なる行削除コマンドだと思っていて、削除以外の用途を長らく見落としていました。設定ファイルの中で特定のキーだけに同じ編集を加えたくてnを押しながらノーマルモードの操作を何度も繰り返していたのですが、記録したマクロを:g/pattern/normal @qで一括再生すれば同じ結果を一発で得られると気づいてから、行に対する繰り返し操作を見つけたらまず:gを思い出す癖がつきました。
マッチした行を並べ替える・複製する
移動系のExコマンドと組み合わせると、マッチした行をまとめて並べ替えたりコピーしたりできます。
:g/TODO/m$ " マッチした行をファイル末尾へ移動
:g/TODO/t$ " マッチした行をファイル末尾へコピー
m$は「TODOを最後にまとめる」といった並べ替えに使えます。t$はコピーなので元の行はそのまま残ります。1行だけ複製したいならyypで十分ですが、マッチした行がファイル中に散らばっている場合は:gと組み合わせたほうが速く済みます。行の順序をまとめて入れ替えたいときは:sortを使う別記事も参考にしてください。
範囲を指定して:gを使う
:gにも:sと同じように範囲を指定できます。
:1,10g/TODO/d " 1〜10行目だけを対象にする
ファイル全体ではなく一部だけを対象にしたいときに使います。マッチした行を削除している最中に行番号がずれていきますが、対象範囲の判定は実行前に確定するので、削除で行が詰まっても意図した範囲からはみ出すことはありません。ビジュアルモードで範囲を選択してから:を押すと:'<,'>g/が自動的に入力されるので、行番号を数えて指定する必要はありません。
:gと:sの使い分け
:gと:sはどちらもパターンとコマンドラインで完結する点が似ていて混同されやすいですが、役割はまったく違います。:sは行の中身の文字列を置き換えるコマンドで、行そのものは残ります。:gは行を検索してその行に対してコマンドを実行するコマンドで、実行するコマンドがdなら行そのものが消えます。全置換の細かいパターンはこちらの記事にまとめています。
| 観点 | :s | :g |
|---|---|---|
| 操作の単位 | マッチした文字列 | マッチした行 |
| できること | 文字列の置換のみ | 任意のExコマンドの実行 |
| 範囲省略時の対象 | 現在行だけ | ファイル全体 |
| 行を消せるか | 不可(中身が空になるだけ) | :g/pat/dで行ごと消せる |
:s、行そのものを操作したいなら:gです。「行を対象に何かする」と気づいた時点で:gを検討すると、置換パターンをこじらせずに済みます。
まとめ
:g/pattern/dで削除、:v/pattern/dで抽出、:g/pattern/normal!でキー操作の一括適用、この3つを覚えておけば行単位の一括編集はほとんど:gで済ませられます。複数ファイルに同じ処理をかけたいときは:bufdoと組み合わせます。
:bufdo g/TODO/d " 開いている全バッファに:gを適用
:bufdoで次のバッファに移る前に保存を求められて処理が止まる場合は、あらかじめ:set hiddenを実行しておきます。ウィンドウ・バッファ・タブをまとめて操作する方法は別記事で詳しく扱っているので、複数ファイルへの適用を本格的にやりたい場合はそちらも参照してください。:gと:sの役割の違いを意識しておけば、行を消すべきか文字列だけ変えるべきかで迷うことはなくなります。