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の組み合わせによるリファクタリングという技は、磨いていけばはかなり強力な武器になる気がしてきた。