scanコマンドというcliツールを作った。
scanコマンドは、標準入力の各行に対して正規表現を適用し、ほしい部分を取り出すコマンドだ。使い方は以下の通り。
$ scan --help Usage: scan [options] OUTPUT_FORMAT -p [PATTERN] specify regexp -d [DELIMITER] specify delimiter
使用例を見てもらったほうが早いだろう。
使用例
正規表現を適用する例
例えばこういうファイルがあったとする。
$ cat data.txt hogehoge_nyan hohho_nyan
これに対して、アンダースコアの左だけを取り出す正規表現を適用するには、以下のようにする。
$ cat data.txt | scan -p "(.+?)_.+" {1} hogehoge hohho
アンダースコアの左と右を、 ,
で繋いで出力したいときは以下のようにする。
$ cat data.txt | scan -p "(.+?)_(.+)" {1},{2} hogehoge,nyan hohho,nyan
複雑な正規表現を適用するときは、名前付きキャプチャも使用できる。
$ cat data.txt | scan -p "(?<name1>.+?)_(?<name2>.+)" {name1}:{name2} hogehoge:nyan hohho:nyan
正規表現エンジンはRuby標準のものをそのまま使っているので、正規表現の詳しい仕様についてはリファレンスをあたってください。
区切り文字を指定する例
また、正規表現を使うのではなく、区切り文字を指定することもできる。
例えばこういうファイルがあったとする。
$ cat hoge.csv aaa,bbb,ccc xxx,yyy,zzz
このとき、区切り文字として ,
を指定して、左から3番目のフィールドを切り出すためには以下のようにする。
$ cat hoge.csv | scan -d , {3} ccc zzz
また、scanコマンドに何もオプションを指定しない場合、区切り文字として \s+
つまり「1つ以上連続する空白文字列」が指定されていると解釈される。
例えばこういうファイルがあるとする。
$ cat hoge.tsv aaa bbb ccc xxx yyy zzz
このファイルを、オプションを何も指定しないscanコマンドに食わせると、空白文字を区切りと解釈して左からn番目の文字列を取り出すことができる。
$ cat hoge.tsv | scan {2} bbb yyy
インストール方法
Ruby 2.6.3 以上が動く環境を前提として、以下のファイルをパスが通った場所に配置して、実行権限を付与すれば動く。依存ライブラリとかはなく、Rubyがあれば動きます。
scan/scan at master · genya0407/scan · GitHub
もう少しいい感じのインストール方法を模索していますが、ひとまずはこれで許してください。
なぜこのコマンドを作ったのか
仕事柄(?)、バグが出たときとかにサーバーのログを漁る必要にかられることがよくある。そういうときに正規表現は便利だ。
$ cat server.log | (アクセスしたユーザーのIDをいい感じに取り出す正規表現) | sort | uniq -c 100 user_id_111 # user_id_111 が100回もアクセスしているのがわかる 10 user_id_222 ..
しかし、正規表現をシュッと書いて値を抜き出す適用するツールが見当たらなかった*1。多分awkとかperlとかのワンライナーで正規表現をかけるとは思うのだが、僕はawkもperlも使い方がわからないし、言語ごとに存在すると思われる正規表現の方言を覚えるのもなんだかなあという気持ちだった。
僕が一番使えるプログラミング言語はRubyなので、一時期はRubyのワンライナーを書くということをやっていた。
$ cat server.log | ruby -ne 'puts $_[/user_id: (.+)\s+/, 1]' | sort | uniq -c
これも割といい線行ってるとは思うが、正規表現で値を抜き出したいだけなのに ruby -ne
とか puts
とか $_
とか書くのはイケてない。
次に使ってたのはrargsというツールで、これは限りなく正解に近い。
rargsは、正規表現を指定して文字列を抜き出すことができ、抜き出した文字列を使ったコマンドを実行することができる。正規表現の代わりに区切り文字を指定することもできる。
$ ls *.bak | rargs -p '(.*)\.bak' mv {0} {1} $ cat hoge.csv | rargs -d , echo {2}
しかし、rargsはコマンドを実行する都合上、入力される行の数だけプロセスを立ち上げる必要がある。環境にもよるが、プロセスの立ち上げはそこそこヘビーな処理で、業務で使っているMacBook Proではここがものすごく重かった*2。そのため、長めのログファイルをrargsに食わせると、ちょっと現実的ではないぐらい集計に時間がかかってしまう状態になっていた。
私が望む用途(=ログの集計)ではコマンドを実行する必要はない。そのため、rargsからコマンド実行の機能を削り、高速に正規表現を適用するだけのコマンドを作ればよいだろう、という発想に至った。
そして、今回説明したscanコマンドを作った。
まとめ
標準入力に正規表現をシュッと適用して、好きなフォーマットに整形して出力するコマンドである scan
を作った。これは、ログの集計・整形などに使うことができ、実用的なレベルには高速であり、自身も便利に使っている。