Apache のアクセスログを TSV 形式 (or LTSV) に変換するスクリプトが欲しかったので書いてみました。
用途としては、 grep や awk の組み合わせでは面倒だけれども、 Flume や Fluentd などをインストールする規模でもないサーバーを想定しています。 ちょっとした開発用のサーバーや、社内サーバーのアクセス状況を見るには十分かな、と思います。
似たような処理を書く機会が増えてきましたので、Python のパッケージを作ってみました。
Unix 系のツールのお作法として、次のようなルールがあると思います。
grep, sed, awk, sort などはこれらの挙動をしますので、 パイプライン処理を簡単に実現できます。
上記の条件に沿ったコードを書くこと自体は難しくないと思いますが、 存在しないファイルが指定されたときにどうするか? 入力テキストの文字エンコーディングを指定したときにどうするか? ログ出力のレベルをどうするか? といったことを考え始めると、 それはそれで煩雑になっていくと思います。 1週間に2回以上もそんなことを考えているとうんざりするでしょうし、 そういったエラー処理を考慮しないスクリプトを運用するのも大変でしょう。
上記のパッケージを使うと、Python を素のまま使うよりは簡単に記述できると思います。 Python 2.7 と Python 3.3 で動かせるように書いているつもりです。
この記事ではパッケージの導入方法と簡単なスクリプトを紹介してみたいと思います。
Python の引数処理モジュールである argparse を使ってみます。 そろそろ Python 3.3 で作業を始めるべきでしょうし、いつまでも Python 2.4 のことを考えていても窮屈なためです。
Python の標準ライブラリを使った引数処理には、次のような段階があると思います。
Argparse Tutorial には次のように記載されています。
There’s two other modules that fulfill the same task, namely getopt (an equivalent for getopt() from the C language) and the deprecated optparse. Note also that argparse is based on optparse, and therefore very similar in terms of usage.
ざっくり日本語訳 :
同じタスクを満足にこなすためには他にも2つのモジュールがあります。 C 言語の getopt() と等価な getopt と、非推奨になった optparse です。 argparse は optparse をベースにしていますので、使い方は非常に似ています。
その他にもサードパーティのライブラリを使う方法があります。 何らかのフレームワークに付随するものや gflags を使う方法はこちらの記事で少しずつ触れています。
"There should be one-- and preferably only one --obvious way to do it." (* Zen of Python) という精神の通りにはいきませんが、 やはり標準ライブラリに沿って実装する方法を身につけておくことは大切だと思いますので、 argparse を使います。
Python Conference Japan 2012 で機会を頂けたので発表してきました。 45分枠で70名くらいの方にご参加頂きました。
資料は Google Docs で作成してあり、プレゼンテーションは YouTube にアップされています。 プレゼンの終わりの方でノートパソコンが不調になってしまって5分くらい止まってしまい、せっかく聞きにきて頂いた方には申し訳ない感じです。 また機会があれば、もうちょっとちゃんと準備しておきたいと思います。。。
"csvkit" には csvsql というコマンドラインツールが付属しています。 オプションを調整するとデータを登録できます。
データベースに付属するツールで CSV を登録することもできますが、 複数のデータベースを切り替えて利用したい場合、 O/R マッパーを使って SQL を隠蔽しておくと便利なこともあります。 "csvkit" は SQLAlchemy を利用できますので、データベース接続文字列を切り替えることで ツールの接続先を変更できます。
そこで、実際に SQLite と MySQL に郵便番号データを登録してみます。
"csvkit" は CSV ファイルの処理や変換のためのユーティリティ群です。 Python で記述されており、コマンドラインスクリプトとして利用する方法と、 Python のモジュールとして利用する方法があります。
ソースコードのリポジトリとドキュメントはこちらにあります。
ちょっと、CSV のデータから KML ファイルを作成してみます。
Waf を使うと、Python で色々な処理を記述できます。 Waf は普通は C 言語などのコンパイルに使いますが、 ある条件に従って何らかの処理を実行できると便利ですので、他のことにも流用してみます。 以前に、Solr をダウンロードして解凍できることを確認しましたので、 今回は、そこから Solr を起動させます。
たまには Web 系技術もキャッチアップした方が良いかな、と思っていたら、 Indexed Database API のサンプルがありましたので模写しました。
ブログの記事は reST (reStructuredText) で書いて HTML に変換していますが、 パソコンを買い替えたので環境を移行しなければなりません。 ということで、メモを書いておきます。
「ふつうの Haskell プログラミング」をつらっと読んで Haskell を書いてみたメモです。 Haskell のメリットとこの本を読む目的は書籍の 9 ページにざっくりとまとめられています。
遅延評価とは、引数を必要なときだけ評価するという、ただそれだけのことです。 それだけのことですが、それがどういう意味を持つかは、実際に体験してみないとよくわかりません。 ある意味、本書は遅延評価の嬉しさを書くためにあると言ってもよいでしょう。
注力すべきことだけを記述するため、余計なことは極力排除し、扱う問題をできるかぎり細分化していきます。 分割統治の「分割」を徹底的にこだわるイメージでしょうか。 書籍の後半では、Haskell のつまずきポイントである「モナド」について「床下配線」と表現しています。 関係ないことはまとめてどこかに押し込める、という雰囲気が分かりやすいですね。
書籍を離れてみると、Haskell とは関数型言語で云々かんぬん... という話もぽつぽつありますが、 個人的には次の一文がもっともしっくりくるまとめでした。
"Haskell は簡単なことと難しいことが入れ替わったような言語で..." / Haskell とは http://www.shido.info/hs/haskell1.html
構文や使い方、それぞれの概念は書籍を読むとして、ここでは簡単なプログラムを書いてみます。 まずは実行環境を整えて、日本語を扱えるようにします。
次の2つの記事を参考にして環境を準備しました。 手元の環境は Mac OSX 10.5 です (ちょっと古いので買い替えようかな)。
macports からインストールします。 おそらく他のパッケージ管理システムでも類似のパッケージが存在すると思います。
$ sudo port install ghc $ sudo port install hs-utf8-string
ghc-pkg でパッケージ一覧を表示させてみます。
$ ghc-pkg list
/opt/local/lib/ghc-6.10.4/./package.conf:
Cabal-1.6.0.3, HUnit-1.2.0.3, QuickCheck-1.2.0.0, array-0.2.0.0,
base-3.0.3.1, base-4.1.0.0, bytestring-0.9.1.4, containers-0.2.0.1,
directory-1.0.0.3, (dph-base-0.3), (dph-par-0.3),
(dph-prim-interface-0.3), (dph-prim-par-0.3), (dph-prim-seq-0.3),
(dph-seq-0.3), extensible-exceptions-0.1.1.0, filepath-1.1.0.2,
(ghc-6.10.4), ghc-prim-0.1.0.0, haddock-2.4.2, haskell-src-1.0.1.3,
haskell98-1.0.1.0, hpc-0.5.0.3, html-1.0.1.2, integer-0.1.0.1,
mtl-1.1.0.2, network-2.2.1.2, old-locale-1.0.0.1, old-time-1.0.0.2,
packedstring-0.1.0.1, parallel-1.1.0.1, parsec-2.1.0.1,
pretty-1.0.1.0, process-1.0.1.1, random-1.0.0.1,
regex-base-0.72.0.2, regex-compat-0.71.0.1, regex-posix-0.72.0.3,
rts-1.0, stm-2.1.1.2, syb-0.1.0.1, template-haskell-2.3.0.1,
time-1.1.4, unix-2.3.2.0, utf8-string-0.3.5, xhtml-3000.2.0.1
色々ありますが、最後の一行に utf8-string を確認できます。 インタープリタを起動して Prelude を確認できれば良いと思います。
$ ghci GHCi, version 6.10.4: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. Prelude>
実際に Haskell プログラムを書いてみます。 ここでは、カンマで区切られたテキストファイルにおいて、 フィールドが「味スタ」の場合に「味の素スタジアム」に変換するものを作ります。 正確な表現ではありませんが、実現したいことは次のことです。
そもそも "CSV" とは何か?という問題には深入りしません。 RFC4180 とエクセルの互換性とか、ロケールがフランスなどの場合には小数点がカンマだから云々かんぬん... という話に立ち入ってしまうと大変なので、上述の仕様に簡略化しました。 ダブルクォーテションでの引用やエスケープ、区切り文字以外の用途でのカンマは存在しないものとします。 フィールドの中に改行も許しません。
なお、Haskell で CSV をきちんと扱う場合にはこちらが参考になるでしょう。 Parsec を使います。
ということで、実装してみました。 たぶんもっと効率的な書き方があると思いますが、一番最初はこんな感じで力尽きました。 組み込み関数 (or Prelude で定義されているもの) をもう少し調べた方が良さそうです。
import qualified System.IO.UTF8 as U
main = do cs <- U.getContents
U.putStr $ unlines $ parse $ lines cs
parse :: [String] -> [String]
parse ss = map replace ss
replace :: String -> String
replace cs = join $ map rewrite $ split cs
split :: String -> [String]
split cs = words $ map csv2tsv cs
where
csv2tsv :: Char -> Char
csv2tsv ',' = '\t'
csv2tsv c = c
join :: [String] -> String
join ss = map space2csv $ unwords ss
where
space2csv :: Char -> Char
space2csv ' ' = ','
space2csv c = c
rewrite :: String -> String
rewrite "味スタ" = "味の素スタジアム"
rewrite cs = cs
Haskell の入門書を読みながら簡単な入出力のプログラムを書いてみました。 扱っているデータ型がどのデータ型のリストなのか、ということを理解するまでに時間がかかりました。 しかし、ひとつひとつの関数の型を書き下していけば理解できそうであることも分かりましたので、 日常的に書き続けるかどうかの問題とも言えます。
冒頭の書籍の「ふつうのまえがき」でも述べられているように、今、Haskell (もしくはその他の関数型言語) を学ぶ意義は、 異なる知見を得るためという部分が大きいと思います。
本書が目指すのは、Haskell という未知の言語の血を導入することでみなさんにプログラマとしてレベルアップしてもらうことです。 たとえ同じ言語を使い続けるとしても、Haskell の知見を活かせばプログラムはよりよくなるでしょう。 それこそが新しい言語を学ぶことの利益であり、また本書の狙いでもあるのです。
もちろん業務でゴリゴリと使っている人もたくさんいると思いますが、まだまだ「多数派」ではないのかな、と。
コマンドライン引数の扱い方にはいくつかの方法がありますのでメモしておきます。 もちろん sys.argv を自前で解析すれば何とでもなりますが、その解析を頑張りたくはありませんね、という動機です。
AIR 2.5 は Android 2.2 (Froyo) に対応するようになりました。 データを失いながらもバージョンアップした手元の Xperia は未だに 2.1 ですが、いつかは 2.2 になると思います。 そんな未来に備えて AIR アプリを作れる環境を整えておきます。
実機で AIR を動かせる携帯端末を持っていませんので、まずは HTML+JavaScript+CSS で普通のデスクトップアプリを作ることにします。 AIR アプリを作る場合には Flash もしくは Flex で ActionScript も使えますが、 XML (MXML) の中にスクリプトを書くのは苦手なので JavaScript にします。 こちらの方法で実装しておくと、通常の Web アプリケーションや Chrome 拡張などを流用できるかもしれない、という淡い期待もあります。
Adobe のサイトが非常に整備されていますので、通常のリファレンスだけでなくチュートリアルもここを見るのが良いでしょう。 Google で検索すると上位にきますので、必然的に本家のチュートリアルを見ることになると思います。 ただし、SDK のバージョンによってアプリケーションディスクリプタの名前空間が異なりますので、バージョンが最新であるかを確認する必要があります。 特に、バージョン 2.5 からはバージョンの記法が変わりましたので注意が必要です。
セットアップの手順は次のようになりますので、順番に見ていきましょう。
いずれのチュートリアルでも、SDK の bin ディレクトリにパスを通して... という書き方なので、最初から好き嫌いが別れるかもしれません。 複数のマシンで開発する場合は揃えた方が良いと思いますので、AIR の SDK は次の場所にインストール (ZIP を展開) します。 (もちろん、標準的な置き場所があるならそちらの方がベターですが、見つけられませんでした。)
| Windows の場合: | C:\Program Files\Adobe\AdobeAIRSDK-2.5 |
|---|---|
| Linux の場合: | /usr/local/adobe/AdobeAIRSDK-2.5 |
| Mac OSX の場合: | /Applications/Adobe/AdobeAIRSDK-2.5 |
Adobe のサイト から AIR SDK の ZIP をダウンロードして展開します。 OS によってダウンロードすべきファイルが異なります。 展開したアーカイブにはバージョン番号が付いていませんので、後から識別できるように手動で変更します。 必要があればシンボリックリンクなどを作成してデフォルトの SDK を指定できるようにすると良いかもしれません。
例
$ sudo ln -s /usr/local/adobe/AdobeAIRSDK-2.5 /usr/local/adobe/AdobeAIRSDK
AIR のプロジェクトには、XML 形式のアプリケーションのディスクリプタファイルが必要です。 後でコマンドライン引数に与えることになりますので名前は何でも良いと思いますが、ここでは application.xml としておきます。 ディスクリプタファイルは適当なチュートリアルからコピーしても構わないと思いますが、 SDK に雛形が付属していますので、そこからコピーするのが良いと思います。 初めは分からないことも多く書かれていて非常に冗長ですが、後で機能を拡張するときに役に立つはずです。
JavaScript でアプリケーションを記述する場合は air オブジェクトを利用可能にしてくれるエイリアスファイル (AIRAliases.js) もあると便利です。 これも SDK に付属していますので、そこからコピーします。
最後に、アプリケーションの入り口となる HTML ファイルが必要です。 もちろん、このファイルの名前も任意に指定できます。 試しに プロジェクト名.html としておきます。
ここまでの手順を順番に実行すれば良いのですが、手動でやると間違えてしまうこともありますので セットアップ用スクリプト を用意します。 Python がインストールされている環境で次のように実行します。
$ wget --no-check-certificate -O air-quickstart.py https://github.com/skitazaki/sandbox/raw/master/src/d74.py $ python air-quickstart.py -d "C:\Program Files\Adobe\AdobeAIRSDK-2.5" sample C:\Program Files\Adobe\AdobeAIRSDK-2.5\bin\adl application.xml C:\Program Files\Adobe\AdobeAIRSDK-2.5\bin\adt -certificate -cn SelfSigned 1024-RSA sampleCert.pfx samplePassword C:\Program Files\Adobe\AdobeAIRSDK-2.5\bin\adt -package -certificate sampleCert.pfx -password samplePassword sample.air application.xml sample.html AIRAliases.js $ ls sample AIRAliases.js application.xml sample.html
オプション引数 -d に SDK をインストールしたディレクトリを指定して、コマンドライン引数でプロジェクト名を指定します。 3つのコマンドが出力されますが、このコマンドでアプリケーションの実行やパッケージングが可能です。 最後にプロジェクトディレクトリの中身を確認すると、上述のファイルが生成されていることが分かります。
ディスクリプタファイル (application.xml) を編集します。 先ほどのセットアップ用スクリプトでこれも実行できるようにしても良いかもしれません。
変更すべきは次の4つの要素です。
次に、パッケージを作成します。 自己署名ファイルを作成し、それを参照して .air ファイルを作成します。 コマンドオプションを覚えるのは大変なので、Windows の場合は make.bat 、Unix 系の場合は Makefile を書きます。 Sphinx と同じように、アーキテクチャを問わずに make とタイプするだけでよくなります。 また、ここで絶対パスを使うようにしておくと環境変数 PATH を汚染する必要がありません。
make.bat
@ECHO OFF
REM Command file for AIR application
::set BASEDIR=%PROGRAMFILES(x86)%\Adobe\AdobeAIRSDK-2.5
set BASEDIR=%PROGRAMFILES%\Adobe\AdobeAIRSDK-2.5
set ADT=%BASEDIR%\bin\adt.bat
set ADL=%BASEDIR%\bin\adl.exe
set AIR_DESCRIPTOR=application.xml
set AIR_PACKAGE=sample.air
set AIR_SCRIPTS=sample.html AIRAliases.js
set CERTIFICATE=sampleCert.pfx
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. run to start up application
echo. init to make a samle certificate file
echo. package to make a ".air" package
goto end
)
if "%1" == "run" (
echo. "%ADL%"
"%ADL%" "%AIR_DESCRIPTOR%"
goto end
)
if "%1" == "init" (
"%ADT%" -certificate -cn SelfSigned 1024-RSA %CERTIFICATE% samplePassword
goto end
)
if "%1" == "package" (
"%ADT%" -package -storetype pkcs12 -keystore %CERTIFICATE% -storepass samplePassword "%AIR_PACKAGE%" "%AIR_DESCRIPTOR%" %AIR_SCRIPTS%
goto end
)
:end
Makefile
BASEDIR = /Applications/Adobe/AdobeAIRSDK-2.5
#BASEDIR = /usr/local/adobe/AdobeAIRSDK-2.5
ADT = $(BASEDIR)/bin/adt
ADL = $(BASEDIR)/bin/adl
AIR_DESCRIPTOR = application.xml
AIR_PACKAGE = sample.air
AIR_SCRIPTS = sample.html AIRAliases.js
CERTIFICATE = sampleCert.pfx
all:
$(ADL) $(AIR_DESCRIPTOR)
init:
$(ADT) -certificate -cn SelfSigned 1024-RSA $(CERTIFICATE) samplePassword
package:
$(ADT) -package -storetype pkcs12 -keystore %CERTIFICATE% -storepass samplePassword $(AIR_PACKAGE) $(AIR_DESCRIPTOR) $(AIR_SCRIPTS)
.phony: all
あとはコマンドプロンプトなどから次のように実行します。 sample.air が生成されれば成功です。
$ make init $ make package
パッケージにまとめるファイルを追加する場合には AIR_SCRIPTS に追記していきます。 たとえば jQuery を使う場合には次のようにします。(make.bat の場合)
set AIR_SCRIPTS=sample.html AIRAliases.js jquery-1.4.4.min.js
アンドロイド用のパッケージを生成する場合には -target オプションで apk を指定すれば良いようです。
あと、 make run で動作を確認できます。
AIR の SDK を使って .air パッケージを作成してみました。 調べてみると、想像以上にドキュメント類が充実していて驚きました。 今回は簡単なスクリプトを自作しましたが、この手のことはアプリケーションのフレームワークや統合開発環境 (たとえば Aptana Studio ) に委ねてしまう方が良いでしょう。 とはいえ、実際に何が起こっているかを確認しておくことも大事だと思いますので、好き好きではあります。
「一度書けばどこでも動く」という考えは正しくもありそうでないこともあり、、、というのが現実だと思いますが、 手軽にアプリケーションを作れるようになってきたのは間違いないと思います。 あとは Xperia がバージョン 2.2 にアップデートしてくれると嬉しいですね。
読もうかなぁと思って放置していた英語の記事がいくつかありましたのでまとめておきます。ここ2年くらい何もしてないんじゃないかと思うと、ちょっとげんなりします。
Python: HTML, JavaScript# -*- encoding: utf-8 -*-
def rec(query):
if len(query):
rec(query[1:])
print query
def gen(query):
while len(query):
yield query[0]
query = query[1:]
sample = "hoge"
print "\n\tsample query: %s\n" % sample
print "---------------------------------------- "
print " recursive printing... "
rec(sample)
'''
e
ge
oge
hoge
'''
print "---------------------------------------- "
print " generative printing... "
s = ""
for i in gen(sample):
s += i
print s
'''
h
ho
hog
hoge
'''
ちなみに、手元の「みんなの Python」(250ページ) には、次のような記述があります。yield 文と return 文を、一つの関数で使うことはできません。上記サンプルを書いているときにエラーが出たので読んでみましたが、そりゃそうだ、という話でしたとさ。
$ chmod +x rfc2ipod.sh $ ./rfc2ipod.sh 2616読み終わったら掃除します。
$ ./rfc2ipod.sh clean######################################## split-by-page.awk
{
split(FILENAME, filename, ".")
if($0=="\f" || !output)
output = sprintf("%s-%03d.txt", filename[1], ++page);
else
print $0 >> output;
close(output)
}
rfc2ipod.sh
#!/bin/sh
##
# @file rfc2ipod.sh
# @author Kitazaki, Shigeru
# @see http://developer.apple.com/ipod/iPodNotesFeatureGuideCB.pdf
# @brief Retrieve RFC document via HTTP,
# and split document into page by page.
# This is useful to save in iPod, which is
# restricted 4KB for one text file.
END_POINT=http://www.ietf.org/rfc
SPLITTER=split-by-page.awk
IPODNAME=KSHIGERU
IPOD=/Volumes/$IPODNAME
if [ -n "$1" ];
then
case $1 in
[0-9]*)
if [ -d $IPOD ];
then
doc=rfc$1.txt
wget -O $doc $END_POINT/$doc
awk -f $SPLITTER $doc
mv rfc$1-* $IPOD/Notes
else
echo No iPod device in \'$IPOD\'.
fi
;;
clean)
rm -f rfc*.txt
if [ -d $IPOD ];
then
rm -f $IPOD/Notes/rfc*.txt
fi
;;
*)
echo Enter valid number.
;;
esac
else
echo Usage:
echo '\t' $0 RFC-number
echo '\t' $0 clean
fi
awkが必要ですが、簡単に書けました。しかし、シェルスクリプトではいつもクォーテションに悩まされます。まだまだ書いている量が足りないことを痛感してしまいます。
下記スクリプトの問題点として、Windows環境(cygwin)だとパスに空白が含まれる(Program Files)ためこのままでは使えません。適当に環境に併せて中間処理を挟む必要があります。
#!/bin/sh
# command-list.sh
temporary="temporary.data"
for p in `echo $PATH | awk -F : '{for(i=1; i<=NF; i++) print $i}'`
do
ls $p >> $temporary
done
sort $temporary | awk '{printf "%04d %s\n", NR, $1;}'
rm -f $temporary
手許の環境 (Mac OSX) で実行してみると次のようになりました。
$ ./command-list.sh | wc -l
2205
つまり、利用可能なコマンドは2205個存在しています。自分が使っているものなんて氷山の一角にも満たないことがわかります。
man以外のawkに関するドキュメント→ NR, NFに関しては非常に便利なので覚えておきたい。