読者です 読者をやめる 読者になる 読者になる

カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

テキストデータに親しみたいエンジニアがよく使いそうなコマンド:「zgrep」コマンドへの道

ふとたまたま、
圧縮されたアーカイブログにもgrepがかけられる「zgrep」が便利!」という記事を読んで、こんなコマンドあるんだと思い、どうせならテキストデータ生成から色々コマンド使ってみようじゃないかと思い立ったのが、今回の発端。少しでも黒い画面が好きになる人が増えたら幸いです。

ということで、ちょっと試してみた。

まず検証用データを作ってみる。

まぁ、業務に携わってればログとか色々あるだろうけど、せっかくだしコマンドを色々使って、データ作ってみましょう。

まずある程度固まってるデータとして、郵政のページから郵便番号のCSVを取ってくる。wgetとかはテキストデータと関係ないので除外(優しくなくてごめんなさい。書いてて疲れたw)。あと、このCSVはDBサーバに流したりもできるので、便利ですよと。とりあえず解凍。
lzhを解凍するには、lhaというコマンドを使います。こういうとこから持ってきたりすればいいかな。

$ lha x ken_all_rome.lzh
ken_all_rome.csv        - Melted   :  oooooooooooooooooooooooooooooooooooooooooo

んで、ファイルサイズを確認してみる。そのとき使うのが、duコマンド。ちなみに、解凍したらこれぐらいのファイルサイズ。

$ du -sh *
8.4M    ken_all_rome.csv
984K    ken_all_rome.lzh

行数を調べたいときは、パイプを使って、wcに食わせましょう。

$ cat ken_all_rome.csv | wc -l
123008

12万行だと少ないので、増やしてみる。繰り返せばいいだけなので、forを使うんだけど、1から100まで全部書いたら疲れるので、seqコマンドで連番を発生させる。

$for i in `seq 1 100`
>do
>   cat ken_all_rome.csv >| dup_ken.csv
>done

んで、増やしてみた。

$ du -sh *
839M    dup_ken.csv
8.4M    ken_all_rome.csv
984K    ken_all_rome.lzh

当然行数は、100倍の1200万行。

$ cat dup_ken.csv | wc -l
12300800

そしたら、zgrepのためにgzipで圧縮する。オプションなければ固めてくれます。

$ gzip dup_ken.csv

まぁ、妥当な感じでファイルサイズ縮小

$ du -sh *
97M     dup_ken.csv.gz
8.4M    ken_all_rome.csv
984K    ken_all_rome.lzh

一応、中身確認。パイプでheadコマンドに渡すことで、先頭だけを標準出力に渡してくれます。

$ gzip -dc dup_ken.csv.gz | head
01101,"0600000","IKANIKEISAIGANAIBAAI","CHUO-KU SAPPORO-SHI","HOKKAIDO",0,0,0,0,0,0
01101,"0640941","ASAHIGAOKA","CHUO-KU SAPPORO-SHI","HOKKAIDO",0,0,1,0,0,0
01101,"0600041","ODORIHIGASHI","CHUO-KU SAPPORO-SHI","HOKKAIDO",0,0,1,0,0,0
01101,"0600042","ODORINISHI(1-19-CHOME)","CHUO-KU SAPPORO-SHI","HOKKAIDO",1,0,1,0,0,0
01101,"0640820","ODORINISHI(20-28-CHOME)","CHUO-KU SAPPORO-SHI","HOKKAIDO",1,0,1,0,0,0
01101,"0600031","KITA1-JOHIGASHI","CHUO-KU SAPPORO-SHI","HOKKAIDO",0,0,1,0,0,0
01101,"0600001","KITA1-JONISHI(1-19-CHOME)","CHUO-KU SAPPORO-SHI","HOKKAIDO",1,0,1,0,0,0
01101,"0640821","KITA1-JONISHI(20-28-CHOME)","CHUO-KU SAPPORO-SHI","HOKKAIDO",1,0,1,0,0,0
01101,"0600032","KITA2-JOHIGASHI","CHUO-KU SAPPORO-SHI","HOKKAIDO",0,0,1,0,0,0
01101,"0600002","KITA2-JONISHI(1-19-CHOME)","CHUO-KU SAPPORO-SHI","HOKKAIDO",1,0,1,0,0,0

どんだけ早いのか検証してみる

ということで検証データを作ったので、それを使って検証してみましょう。

適当に思いついた条件で検索かける。パイプでfgrepコマンドで検索条件を指定すればよいだけ。wcコマンドは前述したとおり。

$ gzip -dc dup_ken.csv.gz | fgrep '060' | wc -l
172500

実行時間計るのはtimeコマンド。スピードはこれくらい。二回実行してよいのだろうか…

$ time gzip -dc dup_ken.csv.gz | fgrep '060' | wc -l
172500

real    0m11.343s
user    0m10.030s
sys     0m1.314s

個人的に1200万行を11秒で検索するfgrepはすごいなーと感心したり。たいしたCPUじゃないけど、いい時代なもんだ。
(grepでも大して変わらなかったから放置)

んで、やっぱりawk使いたくなったので、無理やり使ってみる。インクリメントさせてもよかったかも。まず行数間違ってないか確認。シングルクォートで正規表現をまず書いて、そのあとに処理する内容を書く。ここではただ表示するだけなので、print。

$ gzip -dc dup_ken.csv.gz | awk '/060/ {print}'  | wc -l
172500

fgrepと一致したので、計測してみた。新たなに加えるコマンドはなし。

$ time gzip -dc dup_ken.csv.gz | awk '/060/ {print}'  | wc -l
172500

real    1m44.781s
user    1m41.886s
sys     0m2.775s

やっぱ、これぐらいはかかっちゃうかぁと。

んで、本日の出番の「zgrep」。zgrep 条件 ファイル名の順に指定する模様。

$ zgrep '060' dup_ken.csv.gz | wc -l
172500

スピードはfgrepと変わらんかった。

$ time zgrep '060' dup_ken.csv.gz | wc -l
172500

real    0m11.396s
user    0m9.982s
sys     0m1.416s

終わりに

まぁ、zgrepを単に使うだけだったら、こんなことする必要一切ないんだけど、どうせなら色々使えたほうが選択肢も増えるので、ついでに色々書いてみました。

速度的な驚きはなかったですな。

こういうとき、どんなシステムコール呼び出されてるのかトレースしたら面白いかもなーと思いつつ、そこまでやってない。すいません… 誰か(ry

まぁ、仕事していればアクセスログとか桁数の多いデータ、手元にあるかもだけど、ぱっと作って、こう色々できるのはわりとコマンドの面白いとこなんじゃないかなーと思ったり。

単にzgrep使いたいだけだったのに、なんでこんな方向に行ってしまったんだろうか… オプションの説明とかどこ行った…
ほかにも、sortとかuniqとか、awkももっと変態的に(ry

P.S.
基礎から学びたい人は、一応真面目に書いた「今春サーバを触っていくのにびくびくしてる人が1週間ですべき7のこと」のほうがよいかもだったり。一応とか…