cut と awk はどちらが高速なのか
どうもハニーポッターです.
先日,社内の勉強会に参加しているときに,「Web サーバをのログを調査するときは,膨大な量のデータから欲しい情報を抽出して効率よく解析しなければならない」という話がありました.
抽出したい情報とは,たとえばアクセスのあったファイル名やアクセス元 IP アドレスなどですが,発表者の方は awk コマンドを使って抽出されていました.
で,ここで1つの素朴な疑問が.
「1行データの中から,特定のフィールドを抽出するだけなら awk コマンド使わなくてもよくね? cut コマンドで十分じゃね?」と.
疑問は検証しなければいけないので「大量データから情報を抽出するには cut コマンドと awk コマンドのどちらが高速か?」を調べてみました.
※この記事は「適材適所でコマンドを使うべきだ」というのが趣旨ですので,決してきのこ・たけのこの争いや,Emacs 対 Vim のエディタ闘争とは関係ありません.
最初に cut コマンドと awk コマンドについておさらいしておきましょう.
cut コマンドは,1行のデータを特定の区切り文字で区切って,特定のフィールドを抽出することができるコマンドです.
参考:【 cut 】 テキスト・ファイルの各行から一部分を取り出す
一方,awk コマンドは,文字列操作をすることができるコマンドで,cut コマンドのように区切り文字を使って,特定のフィールドを抽出することはもちろん,より複雑な文字列操作が可能です.
参考:シェルコマンドを使った処理の効率化は AWKの行(レコード)操作がカギをにぎる
では抽出対象となるデータですが,SSH ハニーポットである Kippo のログ(kippo.log)を使います.(申し訳程度のハニーポット要素)
kippo.log には,攻撃者の IP アドレスや入力したアカウント名・パスワードなどが記録されます.なおログイン後のコンソールログは別ファイルに保存されます.
今回はアカウント名とパスワードの組み合わせを抽出しようと思います.kippo.log には,ログイン試行のログが下記のように記録されます.
2015-03-09 10:05:11+0900 [SSHService ssh-userauth on HoneyPotTransport,134691,***.***.***.***] login attempt [root/rooted] failed
スペース区切りで「日付,時間,[接続情報],login,attempt,[アカウント名とパスワード],ログイン成功・失敗」となります.今回欲しいのは9番目のフィールドのアカウント名とパスワードなので,これを抽出します.
cut コマンドと awk コマンドで区切り文字をスペースとして,9番目のフィールドを抽出する方法は下記の通りです.
cut コマンドの場合 % cut -d’ ‘ -f9 kippo.log |
awk コマンドの場合 % awk -F’ ‘ ‘{ print $9 }’ kippo.log |
そして抽出対象の Kippo のログですが,私のハニーポットで収集したデータを使います.また正規化というわけではないですが,ログイン試行の行のみを対象にします.気になる行数ですが・・・
% wc -l kippo.log
3308613
ということで,約330万回の不正なログインの試みを検知しているデータを使います.
このデータに対して上記の cut コマンドと awk コマンドを実行します.一応それぞれ10回試してみて,その平均値を確認します.
計測環境
MacBook Pro(Retina, Mid 2012), Mac OS X(Yosemite), Intel Core i7 2.6GHz, 16GB Memory, SSD
それではさっくりと結果発表です.なお時間の計測には zsh の time コマンドを使っています.
cut コマンド % for i in {1..10} ; do time cut -d’ ‘ -f9 orig.log > /dev/null ; done cut -d’ ‘ -f9 orig.log > /dev/null 10.41s user 0.14s system 99% cpu 10.559 total cut -d’ ‘ -f9 orig.log > /dev/null 10.38s user 0.14s system 99% cpu 10.527 total cut -d’ ‘ -f9 orig.log > /dev/null 10.36s user 0.14s system 99% cpu 10.505 total cut -d’ ‘ -f9 orig.log > /dev/null 10.38s user 0.14s system 99% cpu 10.528 total cut -d’ ‘ -f9 orig.log > /dev/null 10.38s user 0.14s system 99% cpu 10.532 total cut -d’ ‘ -f9 orig.log > /dev/null 10.33s user 0.14s system 99% cpu 10.476 total cut -d’ ‘ -f9 orig.log > /dev/null 10.37s user 0.14s system 99% cpu 10.517 total cut -d’ ‘ -f9 orig.log > /dev/null 10.41s user 0.14s system 99% cpu 10.558 total cut -d’ ‘ -f9 orig.log > /dev/null 10.34s user 0.14s system 99% cpu 10.483 total cut -d’ ‘ -f9 orig.log > /dev/null 10.44s user 0.14s system 99% cpu 10.590 total |
awk コマンド % for i in {1..10} ; do time awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null ; done awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.52s user 0.20s system 99% cpu 24.751 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.34s user 0.19s system 99% cpu 24.556 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.45s user 0.20s system 99% cpu 24.676 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.35s user 0.20s system 99% cpu 24.578 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.40s user 0.20s system 99% cpu 24.620 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.31s user 0.19s system 99% cpu 24.520 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.40s user 0.20s system 99% cpu 24.619 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.29s user 0.19s system 99% cpu 24.498 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.25s user 0.19s system 99% cpu 24.465 total awk -F’ ‘ ‘{ print $9 }’ orig.log > /dev/null 24.37s user 0.19s system 99% cpu 24.586 total |
total の時間だけを表にまとめます.
cut | awk | |
1回目 | 10.559 | 24.751 |
2回目 | 10.527 | 24.556 |
3回目 | 10.505 | 24.676 |
4回目 | 10.528 | 24.578 |
5回目 | 10.532 | 24.620 |
6回目 | 10.476 | 24.520 |
7回目 | 10.517 | 24.619 |
8回目 | 10.558 | 24.498 |
9回目 | 10.483 | 24.465 |
10回目 | 10.590 | 24.586 |
平均 | 10.528 | 24.587 |
単位:秒
びっくりです.なんと単純な抽出だけであれば,cut コマンドの方が awk コマンドよりも2倍以上高速でした.
結論
単純な抽出作業には cut コマンドを使うと高速に処理できる.
記事の冒頭にも書きましたが,コマンドは適材適所で使うべきであるというのが趣旨です.
複雑な文字列抽出や,シェル芸のような華麗な処理をする場合には awk コマンドが適している場合もありますので,この記事がコマンド選びの参考になれば幸いです.
おまけ
せっかくアカウント名とパスワードの組み合わせを抽出したので,いくつかランキングなどをご紹介しておきます.
・アカウントとパスワードの組み合わせトップ10
root/admin
root/password
root/root
root/q1w2e3
root/12344321
root/a1s2d3f4
root/888888
root/654321
root/z1x2c3v4
root/123654
・入力されたパスワードトップ10
admin
password
root
123456
default
888888
qazwsx
qwer1234
qwe123
q1w2e3
・意識高い系パスワード(個人的に気になったパスワード)
!!!!!!!!!!
(!が10個.そんなに両指を使って文字を打ちたくないのか・・・)
*************
(*が13個.そんなに文字を(ry)
———————————————
(-が45個.途中で何回-を入力したかわからなくなるんじゃ・・・)
!1@2#3$4%5^6&7*8(9)0
(シフトを押したり押さなかったり・・・)
konnichiwa
(こんにちわ.そこは挨拶じゃなくて,パスワードをいれるところですよ・・・)
ninjaman
(アイエエエエ! ニンジャ!? ニンジャナンデ!?)
love4u
(どうも.loveからはじまるパスワードは結構多い)
youcantseeme
(残念.見えてるんだよな・・・)
p1a2s3s4w5o6r7d8
(逆に入力しづらくないですかね?)
p@$$w0rd
(意識高い)