bashにてteeとプロセス置換を使ってパイプを分岐する方法の備忘録です。
bashのパイプラインはテキスト処理に便利ですね。ビッグデータが普通に取り扱われるようになった昨今ですが、システムに組み込み済みの処理はともかく、アドホックなデータ分析業務の場合はまだまだテキストファイルでデータを受け渡しすることが多いです。そうすると、データが巨大な場合はそれをちょっと確認するだけでも一苦労もふた苦労もあったりして、そんなときにbashのテキスト処理系コマンドが威力を発揮します。
ここで、途中まで同じで、後ろが異なる処理を複数やりたい場合がありました。例えば、
zcat file0.gz | ./script0 | ./script1 > file1
zcat file0.gz | ./script0 | ./script2 > file2
zcat file0.gz | ./script0 | ./script3 > file3
のようなケースです。
ここで、file0.gzが小さくて、script0が一瞬で終わるようなものだったらどうってことないのですが、数ギガとか数十ギガという巨大なファイルを取り扱う場合には、このscript0を三回繰り返すのが勿体ない。かといって、script0を適用した結果を保存しておくにはディスクスペースが勿体ない。
そこで、teeを使ってパイプラインを分岐し、それぞれ結果を出力します。
zcat file0.gz | ./script0 | tee >(./script1 > file1) \
| tee >(./script2 | file2) | ./script3 > file3
こうすると、file0の展開ファイルを作る必要が無く、また、script0を適用する回数も一回で済み、やりたい三つの作業を実行することができます。繰り返しになりますが、file0.gzが小さかったり、script0の処理が軽かったりすればなにもこんな面倒なことをしなくて済むのですが、巨大で重い場合はこれだけでかなりの時間節約になります。
なお、各scriptがメモリを大きく食う場合はメモリ不足に要注意です。そもそもパイプラインは一行単位のテキスト処理であるケースが多いですから、その場合はあまり気にしなくても大丈夫だろうと思います。
ちなみにこれを何に使ったかというと、あるPOSデータを記した巨大なcsvファイルの整形です。このファイルには、商品情報と店舗情報がマスターになっておらず、一つのテーブルにまとめて書いてありましたので、
zcat pos.csv.gz | nkf -Sw | sed -e 's/,/\t/g' -e 's/\"//g' \
| tee >(cut -f 3,4,5,6 | sort -u > item.txt) \
| tee >(cut -f 7,8,9,10 | sort -u > shop.txt) \
| cut -f 1,2,3,7,11,12 | gzip > transaction.txt.gz
こうやって商品マスター、店舗マスター、トランザクションの三つに分割しています。このcsvはたまたま要素内に半角カンマがないことがわかっていたので、切り出しは単純にカンマ区切りでOKでしたが、ダブルクオーテーションの中にカンマや改行が入っていたらもっと厄介なことになっていました。カラム3と7がそれぞれ商品マスタと店舗マスタの固有IDです。pos.csv.gzはめちゃめちゃ巨大なので、展開したデータを置いておきたくないですし、さらに文字コードを変換したデータを置くのも厳しいので、展開と文字コード変換を一度だけ実施した上で三つの作業を同時にやりたかった、というわけです。
posted by jinya at 19:59|
Comment(0)
|
日記
|

|