grep, awk, sedを使って「マッチした行とその下N行」を削除する

最近はシェル芸を駆使したリファクタリングにはまっている。

簡単なリファクタリングならIDEでもできるのだけど、それだと固有のIDEとか言語に依存してしまうし込み入ったことは対応できない。

シェル芸によるコード編集を身に着けておけば対象となる言語や時代を超えて使えるので、自己投資としては悪くないと思う。

下記のようなコードを一括で削除したいと思った。

if ($foo === null) {
    return null;
}

こういうとき、エディタで手作業でやりがちなのだけど、手作業したら負けかなと思ってgrep, awk, sedを駆使してやってみた。

(ちなみにここでsedと言ってるのはGNU Sedのことです。)

やりかた

git grep -i -n   'foo === null' | awk -F: '{print "sed -i -e " $2 "," $2+3 "d  "  $1}' | bash

解説

grep -n でマッチした行数を表示させる

$ git grep -i -n 'view === null'
src/Plugin/Smarty/block.form.php:40:        if ($view === null) {
src/Plugin/Smarty/function.form_input.php:19:    if ($view === null) {
src/Plugin/Smarty/function.form_name.php:19:    if ($view === null) {
src/Plugin/Smarty/function.form_submit.php:10:    if ($view === null) {

(ここではgit grepを使ったけど、grepでも同じようにできるはず)

awk -F: でコロンの左と右をパース

パースして得られた値は$1と$2に格納される

「マッチした行から下3行目」という値を得るには、awkのスクリプト部で$2+3のように算術演算すればよい

sedでN行目からM行目までを削除

例: 40行目から43行目までを削除

sed -i -e 40,43d file.php

awkでsedコマンドを組み立ててbashに実行させる

awkの出力を、実行可能なsedワンライナーとなるようにする。

$ git grep -i -n   'view === null' | awk -F: '{print "sed -i -e " $2 "," $2+3 "d  "  $1}'
sed -i -e 40,43d  src/Plugin/Smarty/block.form.php
sed -i -e 19,22d  src/Plugin/Smarty/function.form_input.php
sed -i -e 19,22d  src/Plugin/Smarty/function.form_name.php
sed -i -e 10,13d  src/Plugin/Smarty/function.form_submit.php

あとはこれをbashに食わせればOK

感想

1行のワンライナーで結構なタスクができるものだと再発見した。 上の例ではawkでsedコマンドを組み立ててるのがちょっとダサい感じがあるけど、もっと学習すればスマートな書き方が見つかるかもしれない。 awk/sedの組み合わせによるリファクタリングという技は、磨いていけばはかなり強力な武器になる気がしてきた。

カテゴリ: