Vimで行を並べ替える(:sort)と重複行を消す方法
設定ファイルの環境変数一覧やCSSのプロパティを並べ替えたいだけなのに、外部ツールにコピペしていました。:sortだけでかなりのことができます。
目次(12項目)
| 指定 | これだけ覚える |
|---|---|
u | 重複行を消しながら並べ替える(フラグ) |
n | 数値として並べ替える(フラグ) |
i | 大文字小文字を区別しない(フラグ) |
:sort! | 降順にする |
:uniq | 並べ替えずに、隣り合った重複だけを消す |
基本の並べ替え
:sort " 昇順(行頭文字から)
:sort! " 降順
範囲を指定しなければファイル全体が対象になります。ビジュアルモードで行を選択してから実行すれば、その範囲だけを並べ替えられます。
重複行を消しながら並べ替える
:sort u " 昇順+重複排除
:sort! u " 降順+重複排除
uフラグを付けると、並べ替えと同時に重複した行をまとめて1行にしてくれます。設定ファイルから重複したインポート文を消す、リストアップした値の一覧からユニークなものだけを残す、といった用途に便利です。
banana apple banana cherry apple
:sort uapple banana cherry
数値として並べ替える
:sort n
デフォルトの並べ替えは行頭からの文字列比較になるため、2と10を比べると文字列としては10が先に来てしまいます。数値として正しい順序で並べたい場合はnフラグを付けます。
大文字小文字を区別せずに並べ替える
:sort i
デフォルトでは大文字が小文字より前に来る辞書順で並ぶため、Appleとbananaが期待と違う順序に見えることがあります。iフラグを付けると大文字小文字を無視して並べ替えられるので、人が読みやすい順序にしたいときに使えます。
特定のパターンを基準に並べ替える
:sort /パターン/
行頭からではなく、指定した正規表現パターンにマッチした部分より後ろを基準にソートしたい場合は、パターンを/で挟んで指定します。たとえばkey=value形式の行をvalue部分で並べ替えたいときなどに使えます。
:sortで昇順、重複を消したいならu、数値として並べたいならnを追加します。範囲を絞りたいときはビジュアル選択してから実行します。
重複したimport文が溜まっていくファイルで、以前は目視で1行ずつ確認して消していました。:sort uを知ってからは、範囲を選択して1コマンドで整理できるようになり、地道な作業がほぼ一瞬で終わるようになりました。数値ソートのnフラグを知らずに文字列順で並べて混乱したこともあり、覚えておいてよかったと思っています。
正規表現との組み合わせ
:sort /^\s*/
パターンを指定すると、そのパターンにマッチした部分を無視して、それより後ろの文字列を基準に並べ替えます。行頭の空白(インデント)を無視して中身のテキストだけで並べたい、といった場面で役立ちます。インデントの深さがバラバラなコードの断片を整理したいときにも使える発想で、覚えておくと応用範囲が広がります。パターンさえ工夫すれば、行頭以外のどんな基準でも並べ替えの軸にできる柔軟さが標準コマンドの強みです。外部ツールを持ち出す前に、まずこの範囲で足りないか確認する癖をつけておいて損はありません。
:sort u と :uniq は結果が違う
重複を消すコマンドは2つあり、消える範囲が違います。:uniq は隣り合った同じ行しかまとめませんが、:sort u は並べ替えたあとで重複を落とすので、離れた場所にある同じ行もまとめて1つになります。
Vim 9.1.1752とNeovim 0.12.4で、banana / apple / banana / cherry / apple / apple / date の7行に両方を試しました。
| コマンド | 結果 |
|---|---|
:uniq | banana / apple / banana / cherry / apple / date(連続していた apple だけが1つに) |
:sort u | apple / banana / cherry / date(全部まとまるが、並び順は失われる) |
:uniq が離れた重複を残すのは不具合ではなく、シェルの uniq と同じ仕様です。どちらも隣り合っているかどうかしか見ないので、全部消したいなら先に並べ替えるか、:sort u を使います。
:uniq " 隣り合った重複だけをまとめる(並び順はそのまま)
:sort u " 並べ替えたうえで重複を全部落とす
並び順を変えずに重複を消す
元の順番を保ったまま重複だけを落としたい、という要件は意外とよくあります。:uniq は並べ替えないので、隣り合った重複を潰すだけならこれで足ります。離れた重複まで落としたいときは、外部コマンドに任せるのが簡単です。
:%!awk '!seen[$0]++'
初めて出てきた行だけを通す書き方で、順番はそのまま残ります。ただし awk が要るので、Windowsの素の環境では動きません。組み込みの :uniq は外部コマンドを呼ばないので、どのOSでも同じように動きます。失敗しても u で戻せます。
:uniq の絞り込み方
:uniq i " 大文字小文字を区別しない
:uniq u " 繰り返されていない行だけを残す
:uniq! " 逆に、繰り返された行だけを残す
apple / APPLE / banana / banana / banana / cherry の6行で試すと、:uniq i は apple / banana / cherry、:uniq u は apple / APPLE / cherry、:uniq! は banana だけになりました。:uniq! は、どの行が重複していたかを洗い出すのに使えます。
:uniq /[^,]*,/ " 1列目を読み飛ばして、その後ろで比べる
パターンを付けると、マッチした部分を読み飛ばして、その後ろを比較します。a,dog / b,dog / c,cat / d,cat / e,cat に上の指定をすると a,dog / c,cat の2行が残りました。逆にマッチした部分そのもので比べたいときは r を添えて :uniq r /[^,]*,/ と書きます。ここを取り違えると1行も消えないので、まず小さい範囲で試すのが安全です。
並べ替えたくない行を避ける
ファイル全体に :sort をかけると、先頭のコメントやヘッダー行まで巻き込まれます。行番号で範囲を指定すれば避けられます。
:2,$sort u " 1行目を残して並べ替える
:'<,'>sort u " 選択範囲だけ
:.,+9sort " カーソル行から10行
ビジュアルモードで選んでから : を押すのがいちばん間違えにくい方法です。範囲が見えている状態で実行できるので、対象を数え違えることがありません。範囲の指定の仕方は置換コマンドの記事と共通です。
まとめ
:sortは昇順・降順・重複排除・数値ソートまで標準機能だけでこなせます。外部ツールに頼らず、Vimの中で完結させたい人は覚えておく価値があります。並べ替えの対象を絞り込みたいときは、:globalでマッチする行だけを集めてから:sortを当てると狙った範囲だけを整理できます。並べ替えではなく中身を書き換えたいなら:substituteが同じように範囲を取れます。