複数行にわたる範囲をgrepしたいときはpcre2grepを使おう

結論

pcre2grep-M オプションを使うと、複数行にわたる範囲を対象にgrepできます。

いきさつ

とあるプロジェクトで広範囲なコードの書き換えが並行して行われていたのですが、案の定派手にコンフリクトします。
その際、レビュワーに「コンフリクトの内容の一覧を出した上で解消してほしい」と依頼されました。
プログラマーたるもの、ちまちまコピペなんてしてられないですよね。

方針

ぼんやりと以下のように考えます。

  • ディレクトリ配下を全なめして、コンフリクト箇所の印である <<<<<<<<>>>>>>>> に囲まれた範囲を抜き出してやればいいなー
    • この時点でほぼgrepを使うことになりそうです
    • ディレクトリ配下の再帰的な探索はgrepの -r オプションでできます
  • レビューに使うならファイル名と行数もつけないとわかんなくなるなー
    • grepは -r オプションを使えば自動的にファイル名がつきます
    • 行数は -n オプションを使えばつきます
  • 1つのテキストファイルに収めないと読みづらいなー
    • -r オプションを使うので自然とそうなります

grepしてみた

※以下は動きません

grep -nr '<<<<<<<[\s\S]*?>>>>>>>' .

2つ勘違いしています。

  • 通常のgrepはPerl互換正規表現 (上記の \s \S ) を使えない
    • 一応 GNU grep には -P オプションがありますが、macにもともと入ってるやつはBSD版なので使えません……
  • 通常のgrepは複数行にわたる範囲を検索できない
    • もともと「行を検索する」コマンドなので……

まとめて解消していく

pcre2grep を使います。

pcreとは Perl-compatible Regular Expressions 、すなわちPerl互換正規表現のことです。

pcre2grepには -M, --multiline というオプションがあり、その名の通り複数行の範囲を検索してくれます。
というわけで、以下のようなコマンドになります。

pcre2grep -Mnr '<<<<<<<[\s\S]*?>>>>>>>' .

以下のような出力が得られます。素敵ですね。

./hoge.txt
27:<<<<<<< HEAD
ほげほげ
=======
ふがふが
>>>>>>> topic

./piyo/piyo.txt
5:<<<<<<< HEAD
ぴよぴよ
=======
ぽよぽよ
>>>>>>> topic
20:<<<<<<< HEAD
ぴよぴよぴよ
=======
ぽよぽよぽよ
>>>>>>> topic

あとはパイプして pbcopy に突っ込むなり、テキストファイルにリダイレクトするなりすればOKです。

(おまけ)不要なディレクトリを除外して速度を上げる

プロジェクトルートで先のコマンドを実行すると、結構時間がかかります。
それもそのはず、一般的なプロジェクトならあるであろう依存性のディレクトリやビルド結果のディレクトリ、果ては .git/ まで舐めているからです。

--exclude-dir というオプションがあるので、それを使って検索対象から除外するフォルダを指定します。
{} で囲んで , で区切ると複数指定できます。

今回のプロジェクトはReact (すなわちNode.js) を使ったプロジェクトだったので、最終的に以下のようになりました。

pcre2grep -Mnr --exclude-dir={.git,build,node_modules} '<<<<<<<[\s\S]*?>>>>>>>' .

今後も面倒なことはなるべく機械にやらせていきたいですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

19 − seven =