AWKのバージョン
$ awk -V
GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2019 Free Software Foundation.
状況
タイトルの通りです。
大量にあるデータを特定の条件で別々のファイルに出力したいなぁって時を想定しています。
想定しているファイルの中身
なぜか走らされている男の子の走っていた時間(time)、速度(speed)、その他の情報が乗っているcsvファイルを想定しています。
実際使う状況で一番多いのはlogファイルとかだと思いますが、なかなかいいのがなかったのでとりあえずこれで。
// time,speed,memo,memo1,memo2
2019-12-24 07:00:00,4.0,寒い,あのね,すごく寒い
2019-12-24 07:01:00,8.0,寒い,あのね,すごく寒い
2019-12-24 07:05:10,7.0,寒い,あのね,すごく寒い
2019-12-24 07:06:20,7.5,寒い,あのね,すごく寒い
2019-12-24 07:09:55,7.5,寒い,あのね,すごく寒い
2019-12-24 07:30:01,4.5,寒い,あのね,すごく寒い
2019-12-24 07:45:34,3.2,寒い,あのね,すごく寒い
2019-12-24 07:55:11,2.0,寒い,あのね,すごく寒い
....(これが続く)
分けたいパターン
今回はspeedが時速5km以下、つまり走るのをおそらくサボっていた時間を見つけるという状況を想定して書いていきます。
コード
もちろん色々なやり方があるとは思いますが、私が書いたコードはこんな感じ。
$ cat -n running_data.csv | awk -F'[\t,,]' '{if($3 < 5){print $0}}' | awk -F'[\t,,]' 'begin{num=1;file=1} {if(num!=$1){file+=1};print $0 > "result"file}'
または
$ cat -n running_data.csv | awk -F'[\t,,]' '$3 < 5' | awk -F'[\t,,]' 'begin{num=1;file=1} {if(num!=$1){file+=1};print $0 > "result"file}'
一行にするとキモいですね。
コードをパイプごとに理解してみる
シェル芸を理解するためには、パイプ毎に出力を確認してみて、どのような動作をしているのかを確認するのが良いらしいので分解してみましょう。
シェル芸勉強会でも、変態的、気持ち悪い、すばらしいコードが書かれていることがありますが、分解して行くと一つ一つの処理はそんなに難しくなかったりします。
1つ目のパイプ処理
catの-nオプションを使って先頭に連番をつけています。
$ cat -n running_data.csv
1 2019-12-24 07:00:00,4,寒い,あのね,すごく寒い
2 2019-12-24 07:01:00,5,寒い,あのね,すごく寒い
3 2019-12-24 07:05:10,6,寒い,あのね,すごく寒い
4 2019-12-24 07:06:20,6,寒い,あのね,すごく寒い
5 2019-12-24 07:09:55,7,寒い,あのね,すごく寒い
6 2019-12-24 07:30:01,8,寒い,あのね,すごく寒い
7 2019-12-24 07:45:34,5,寒い,あのね,すごく寒い
8 2019-12-24 07:55:11,4,寒い,あのね,すごく寒い
...
2つ目のパイプ処理
awkコマンドを使って条件である、速度が5以下になっている行だけを抜き出します。
$ cat -n running_data.csv | awk -F'[\t,,]' '{if($3 < 5){print $0}}'
1 2019-12-24 07:00:00,4,寒い,あのね,すごく寒い
8 2019-12-24 07:55:11,4,寒い,あのね,すごく寒い
....
なお、細かいところまで説明すると、-Fオプションは「タブ」または「,」を区切り文字に指定していて、$3は三行目、$0は列全体を表しています。
3つ目のパイプ処理
さてラスト。
再びawkコマンドを使って、一列目が連番になっている時は同じファイルに出力して、連番になっていない時には次のファイルに書き込みを行います。
$ cat -n running_data.csv | awk -F'[\t,,]' '{if($3 < 5){print $0}}' | awk -F'[\t,,]' 'begin{num=1;file=1} {if(num!=$1){file+=1};print $0 > "result"file;num+=1}'
コピペして他のパターンで利用する場合
連続して●●な条件を絞り込みたい時には、もちろん条件等を変更しなくてはいけませんが、次のコードを少しいじって上げれば行けそうです。
$ cat -n <対象のファイル> | awk -F'\t,<区切り文字>' '<絞り込みたい条件>' | awk -F ' ' 'BEGIN{file=0;num=1;} {if(num+1!=$1){file++;} print $0 >> "<出力先ファイル名>"file;num=$1;}'
まとめ
またまだにわかAwkerなのでもっと勉強します。