awk v.s. bashどっちが強い?@osc2011tokyo
DESCRIPTION
OSC2011 Tokyo/Fallで、日本gnu awkユーザー会の斉藤さんと行ったプレゼンテーションです。awkとbashの馴れ合い、もとい、共存共栄がテーマです。TRANSCRIPT
「シェルスクリプト VS AWK」 どっちが強い!?
プログラミングバトル
日本 gnu awk ユーザー会 斉藤博文
USP友の会 上田隆一
2011年11月19日 OSC2011 Tokyo Fall 2
この発表
• awk と シェルスクリプト(bash) の対決– どっちが強い?(小学生風)
– 同じお題をawkとシェルスクリプトで書く• スクリプトの華麗さを競う
– 対決者• awk: 斉藤博文(日本 gnu awk ユーザー会)
– @hi_saito
• シェルスクリプト: 上田隆一(USP友の会)– @usptomo, @ryuichiueda
2011年11月19日 OSC2011 Tokyo Fall 3
awk
• 1977 年にできたスクリプト言語– GNU awk は 1985 年
• UNIX の創造者の英知の集合– Alfred Aho– Peter Weinberger
– Brian Kernighan
• 実は awk は高速– nawk << Ruby, Perl < gawk << mawk << C
2011年11月19日 OSC2011 Tokyo Fall 4
bashスクリプト• シェルスクリプト
– 手で端末に打つコマンドをファイルに書くだけ
• bash– Linux標準、使ったこと無い人はいないはず
• コマンド使い放題– 超高級プログラミング言語としての側面
• 処理の大半はコマンドに任せる
• bashを端末を叩く人はみんなプログラマアイコン?マスコット?
んなもん無い。
2011年11月19日 OSC2011 Tokyo Fall 5
本日のお題
• Round 1 小技: apache logさばき
• Round 2 中技: CGIで何かアピール
• Round 3 フリー演技
2011年11月19日 OSC2011 Tokyo Fall 6
FS にダブルクォート (") を使え!
1回の表 (ノーアウト)
• ダブルクォートを FS にすると・・・– $1:アクセス元、日付
– $2: 受け取ったコマンド
– $3: ステータスコード
– $6: クライアント情報
Apache のログ解析の極意
ほとんどの解析が便利になるよ!
2011年11月19日 OSC2011 Tokyo Fall 7
awk だけに頼るな!
1回の表 (1 アウト)
• awk だけで書こうとすると結構大変かも。
• shell のパイプで繋げるとデバッグもしやすいね。
ログ解析の極意
最も多く 404 を返しているサイトを探せ!
御題
2011年11月19日 OSC2011 Tokyo Fall 8
1回の表 (2 アウト)#! /bin/gawk -fBEGIN { FS = "\"";}$3 ~ / 404 / { split($1, arr, / /); count[arr[1]]++;}END { for (a in count) { stat_arr[i++] = sprintf("%010s", count[a]) " " a; } num = asort(stat_arr); split(stat_arr[num], url, / /); print url[2];}
awk だけでできるけど、直感的じゃないよね。
2011年11月19日 OSC2011 Tokyo Fall 9
1回の表 (2 アウト)
$ cat access_log | \ awk -F '"' '$3 ~ / 404 /' | \ awk '$0 = $1' | \ sort | \ uniq -c | \ sort -nr | \ awk 'NR == 1 && $0 = $2'
127.0.0.1 # オレかよ!!
分かりやすいし、途中結果も確認しやすいね。
2011年11月19日 OSC2011 Tokyo Fall 10
system() は fflush() することをうまく使え!
1回の表 (2 アウト)
バッファに溜まって出力されない?
$ tail -f access_log |\ awk '{system ("dig -x" $1 " +short")}'
リアルタイムで IP アドレスからホスト名を引け!
御題
2011年11月19日 OSC2011 Tokyo Fall 11
1回の裏• シェルスクリプトでのログ処理
– データを正規化しておくと以後の処理が簡単に000003795 1 114.182.31.102000003795 2 -000003795 3 -000003795 4 20111106 162032000003795 5 GET /usage/ctry_usage_201110.png HTTP/1.1000003795 6 200000003795 7 4168000003795 8 http://www.araibo.com/usage/usage_201110.html000003795 9 Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1000003796 1 66.249.66.18000003796 2 -000003796 3 -000003796 4 20111106 164022000003796 5 GET /paper/ARAIBO_TechnicalReport2006.pdf HTTP/1.1000003796 6 200000003796 7 1801153000003796 8 -000003796 9 Googlebot/2.1 (+http://www.google.com/bot.html)
2011年11月19日 OSC2011 Tokyo Fall 12
1回の裏• 正規化スクリプト
– ...結構awk依存#!/bin/bash -vxdir=/var/www/html/OSCtmp=/tmp/$$
cat $dir/LOG/httpd/access_{log.*,log} |sed -e 's/\(..*\) \(..*\) \(..*\) \[\(..*\)\] "\(..*\)" \(..*\) \(..*\) "\(..*\)" "\(..*\)"$/\1ダァシェリイェス\2ダァシェリイェス\3ダァシェリイェス\4ダァシェリイェス\5ダァシェリイェス\6ダァシェリイェス\7ダァシェリイェス\8ダァシェリイェス\9/' |awk -F"ダァシェリイェス" '{c=1;for(i=1;i<=NF;i++){print NR,c++,$i}}' |awk '{$1=sprintf("%09s",$1);print}' > $tmp-data
awk '$2==4' $tmp-data | tr '/:' ' ' |awk '{print $4,$0}' |sed -f $dir/SYS/MONTH |awk '{print $2,$3,$6$1$4,$7$8$9}'| sort -ms -k1,2 - $tmp-data |awk '$2!=4 || $3!~/:/' > $dir/LOG/ACCESS_LOG
2011年11月19日 OSC2011 Tokyo Fall 13
1回の裏
• 続きは端末で– 404エラーを出したIP
• $ cat ../LOG/ACCESS_LOG | awk '$2==1 || $2==6 ' | awk '{if($2==1){printf("%s ",$0)}else{print}}' | grep "404$" | awk '{print $3}' | sort | uniq -c | awk '{print $2,$1}' | sort -k2,2n
– 閲覧された回数• $ awk '$2==5' ../LOG/ACCESS_LOG | awk '{print $4}' | sort | uniq
-c | egrep "cgi|php|htm|html" | awk '{print $2,$1}' | sort -k2,2n
※難しいでしょうか??いや、出力見ながらパイプをつなげていくだけなので、案外簡単です。
2011年11月19日 OSC2011 Tokyo Fall 14
GET と POST を意識せよ!
2回の表 (ノーアウト)
awk で CGI を作る時の極意
最近の LL だと GET と POST を意識しなければいけないのはオワコンかもしれないけど、知って損なし。
2011年11月19日 OSC2011 Tokyo Fall 15
2回の表 (1 アウト)
• 体重を入力すると、グラフにしていってくれる。
• スマートフォンでアプリ化するほどのものでもないよね。
日々の体重を記録せよ!
御題
2011年11月19日 OSC2011 Tokyo Fall 16
2回の表 (1 アウト)
• 手軽
• テキストベースで扱える
• 出力の種類が多い
Gnuplot を使いこなせ!
グラフの極意
munin とか Webalizer のようなものも作れる!
2011年11月19日 OSC2011 Tokyo Fall 17
2回の表 (2 アウト)
<html><head><title>AWK の CGI サンプル</title></head><body><form method="GET" action="graph.cgi"><input type="text" value="60" name="weight"><input type="submit" value="get" name="submit"></form></body></html>
これは普通の HTML です。
2011年11月19日 OSC2011 Tokyo Fall 18
2回の表 (2 アウト)
#! /bin/gawk -f## graph.cgiBEGIN { data_file = "data.txt"; ORS = "\r\n"; split(ENVIRON["QUERY_STRING"], arr_query, "&"); for (i in arr_query) { split(arr_query[i], arr_query_val, "="); query_name = arr_query_val[1]; query_str = arr_query_val[2]; query[query_name] = query_str; }
GET なので環境変数からデータを格納する。
2011年11月19日 OSC2011 Tokyo Fall 19
2回の表 (2 アウト)
now = strftime("%Y/%m/%d-%H:%M"); printf("%s %s\n", now, query["weight"])>>data_file; close(data_file); print "\set xdata time\n\set timefmt '%Y/%m/%d-%H:%M:%S'\n\set terminal png\n\set yrange [0:100]\n\set xlabel 'Date'\n\set ylabel 'Weight (Kg)'\n\set output 'graph.png'\n\plot '" data_file "' using 1:2 title 'Weight' with lines\n\quit\n\" | "gnuplot > /dev/null";
データを格納し、Gnuplot でグラフを描く。
2011年11月19日 OSC2011 Tokyo Fall 20
2回の表 (2 アウト)
print "Content-Type: text/html"; print ""; print "\<html>\<head>\<title>体重のグラフ</title>\</head>\<body>\<img src='graph.png'>\</body>\</html>\";}
HTML を出力し、画面にグラフを表示する。
詳細は
ブースに
Go!
2011年11月19日 OSC2011 Tokyo Fall 21
2回の裏
• テキストの表をブラウザに出力してみる。– コマンドにしてみました。
#!/bin/bashecho '<table cellspacing="0" border="1">'sed -e 's;^;\t<tr>\n\t\t<td>;'\ -e 's;$;</td>\n\t</tr>;'\ -e 's; ;</td>\n\t\t<td>;g' < /dev/stdinecho '</table>'
html_tableコマンド
2011年11月19日 OSC2011 Tokyo Fall 22
2回の裏• すごく頑丈なアクセスカウンタを作ってきました。
– HTTP出力込みでわずか6行
–原理
• アクセスがあるとカウンタファイルを1バイト増やす
• lsでカウンタファイルの容量を調べて出力
• 排他処理なんてイラネエ。あ、awkを使ってもうた。
#!/bin/bash -vxdir=/home/usp/ACCESS_COUNTERecho -n 1 >> $dir/POMPA/COUNTERecho "Content-type: text/html\n"ls -l $dir/POMPA/COUNTER | awk '{print $5}'exit 0
2011年11月19日 OSC2011 Tokyo Fall 23
2回の裏• ドリルダウン機能つきの表(USP研究所のデモから)
– bashで書いたCGIスクリプト• ユーザの操作履歴をPOSTで受ける
• 操作履歴から表示するレコード、開閉ボタンを準備
• htmlのテンプレートに貼り付けて出力
もっと派手なものが見たければブースへ!!
2011年11月19日 OSC2011 Tokyo Fall 24
3回の表 (そして伝説へ・・・)
igawk は 100 % shell なので注意!
gawk で @include する極意!
• 中間ファイルを作成しないように、直接ソースを gawk に食わせているため、巨大なスクリプトを実行するとエラーになる。orz
– comp.lang.awk の FAQ ・・・
eval gawk $opts -- '"$processed_program"' '"$@"'
2011年11月19日 OSC2011 Tokyo Fall 25
3回の表 (そして伝説へ・・・)
好きな言語で記述せよ!
電卓を作る極意!
calc() {awk "BEGIN {print $*}"}
• これ、マジ便利です!
• Perl, Ruby, Python, etc. 自分の好きな言語でどうぞ!
2011年11月19日 OSC2011 Tokyo Fall 26
3回の表 (そして伝説へ・・・)
100 % ネタです!
shell を作る極意!
awk '{system($0)}'
2011年11月19日 OSC2011 Tokyo Fall 27
3回の表 (そして伝説へ・・・)
length() は length でエラーにならない!
ppencode の極意!
0 : length1 : cos(length)2 : int(exp(cos(length)))3 : ...
• length が引数なしでエラーにならないのはバグとして報告されているので、いつか使えなくなるかも?
詳細は
ブースに
Go!
2011年11月19日 OSC2011 Tokyo Fall 28
3回の表 (そして伝説へ・・・)
根気と気合です!
CMS を作る極意!
• 全て awk で記述された Blis を改良し、RSS も生成するようにしてある。
詳細は
ブースに
Go!
2011年11月19日 OSC2011 Tokyo Fall 29
3回の表 (そして伝説へ・・・)
文字コードを UTF-8 に揃えよ!
ImageMagick を扱う極意!
• UTF-8 ならコマンドラインから文字列を指定して、年賀状とか書けちゃうよ。
詳細は
ブースに
Go!
2011年11月19日 OSC2011 Tokyo Fall 30
3回裏(伝説なのか?)• 他の言語を使う。
– PYTHON.sh, C.sh, RUBY.sh, ...
#!/bin/bash# エクセルシートのB列の値を足す。ruby << FIN | awk 'BEGIN{sum=0}{sum+=$1}END{print sum}'require 'rubygems'require 'spreadsheet'book = Spreadsheet.open './hoge.xls'sheet = book.worksheet 0for i in 0..3 print sheet[i,1] print "\n"endFIN
2011年11月19日 OSC2011 Tokyo Fall 31
3回裏(伝説なのか?)
• webから数値参照で書かれた文字列を取ってくる。
#!/bin/bash
curl $1 |grep '&#' |w3m -dump -T text/html
2011年11月19日 OSC2011 Tokyo Fall 32
3回裏(伝説なのか?)
• クローラを作ってきました。#!/bin/bash -vxmkdir -p ./cachecd ./cacheecho 'http://www.google.co.jp/' > ./url.1for i in 2 3 4 ; do cat ./url.$(( $i - 1 )) | wget -i - cat ./* | sed -e 's|href="\([^"][^"]*\)"|\n\1\n|g' | grep ^http | sort | uniq > ./url.$idone
exit 0
2011年11月19日 OSC2011 Tokyo Fall 33
3回裏(伝説なのか?)
• ブースにて、bashで書かれたシステムをお見せします。
2011年11月19日 OSC2011 Tokyo Fall 34
まとめ
• awkなしでシェルスクリプトを書くのは大変だね
• シェルスクリプトと awk で世界が広がるよ– ということで友好団体になりました。
• 質問はブースへ–隣同士にいます。