トップ 最新 追記

2008-02-01 [長年日記]

_ 加能ガニ

石川県産のズワイガニの名称が加能(かのう)ガニに決まっていたそうです。(知らなかった。)そういえば石川県産のズワイガニには越前ガニとか松葉ガニみたいな名前なかったね。

ちなみにカニは面倒なのであまり好きではありません。でもあえて食べるならズワイだなと思います。日本海側に住んでいて冬の海の味覚を愛でない手はない。

Tags: 日々

2008-02-07 [長年日記]

_ 言語の学習とか利用とか:wtnabeの場合

※ 今回は徹底的に自分語りで行くぜ。興味なかったら無視しておくんな。言語の善し悪しを語る気はないぜ。自分の感じたままを書くぜ。

初めに断っておくと自分は Matz のような言語ヲタでもないし、基本的には C 系の言語しか知らない。関数型言語が流行ったのは知っているけど自分ではモノにできていない。Emacs を使っているので Lisp に触れる機会はあるがこれはほぼ入り口で挫折している。(一応 Gauche とか試してみてはいるんだけど)

というわけで自分はきっと世の中の言語を語る人の中では素人や初心者に近い方だろうと思う。そういう自分が少ない経験を晒すのはかなり恐ろしいことではあるのだが、PHP を使う人間の中にはこういう奴もいる、という一例として読んでもらえると嬉しい。ちなみにオチはないので期待しないように。

現在の状況

2008年2月現在、自分がよく使っている道具はだいたい以下の4つである。

  • awk
  • Perl
  • Ruby
  • PHP

※ awk は言語じゃないという意見もあると思うけど、今はそれは置いといてほしい。

Cを薦めない理由

上に挙げたものより以前にやったのは

  • BASIC
  • FORTRAN
  • CASL
  • C

である。このうち最も遊んだのは BASIC, のちに最も影響を与えたのは間違いなく C である。なぜなら awk, Perl, Ruby, PHP は基本的に C の知識の延長上に学べるから。しかし自分は C を強く薦めることはあまりしない。なぜなら自分が C を実際に必要とすることがないから。OS を書くこともハードウェアを操作することも自分ではやらないし、自分で何かを C で書くということはほとんどない。C の知識は今の自分にとって非常に有益だが、学習コストもそれなりに大きかったし、その結果「できること」にあんまり興味が湧かなかったというのが大きい。(もしかすると自分は基本的に計算機そのものにあまり興味がないのかもしれない。)

今使っている言語の中で、実際に使えるようになって最も感動を覚えたのは awk、次いで Ruby である。だから自分はこの二つの学習を薦める傾向にある。ただしこれを入り口にするのが適切かどうかは自分には判断がつかないでいる。判断はできないが根拠を示すことはできる。今回はそれを書こうと思う。

awk を薦める理由

awk は Unix の `Tools' という概念、パイプの機能と実によくマッチし、小回りの効く非常によくできた美しい道具である。もし Perl, Ruby などフルセットの言語を学習するのは面倒だが、それなりの量のデータを Excel の手作業や VBA や VBS よりももう少しエレガントに、あるいは「軽く」処理したいと思うなら awk はオススメ。行単位にファイルを読み込む、行をカラムに分割して格納するという処理を自動で行ってくれるので、自分の目的のデータの処理だけに集中することができる。

例えばデフォルトでホワイトスペース区切りのデータ

abc def ghijkl

$1 = "abc"
$2 = "def"
$3 = "ghijkl"

と自動的に毎行格納してくれる。$1, $2, ... が気持ち悪いという意見はあると思うが、これは歴史的な経緯ってやつで納得してほしい。気に入らなければ自分で名前を付けた変数に入れ直して処理すればいいだけの話だ。

awk は美しい。awk に好意的な人は恐らくほぼみんな同意してくれるはずだ。awk の美しさは潔さでもある。「現実的な人間は」恐らく awk ですべてを片付けることはしない。しかし潔さは学びやすさでもある。

Perl で苦労した話

awk のあとに Perl を学んだが、やはり最初の印象は「きたねー言語だ」というものだった。この印象は今でもあまり変わらない。ずいぶん気にならなくなったけど。

いちばんはじめは、Perl の習得はあまり苦労しなかった。BASIC のような感覚で使えたから。グローバル変数は使い放題だし、サブルーチンという考え方も BASIC, FORTRAN と大差がなかった。C のような構文ではあるんだけど C と違って関数を組み上げる感じの言語じゃないんだなーという印象だった。今にして思うとこれが苦労の始まりだったんだけど。

ご多分に洩れず、だんだんと昔の自分が書いたコードが読めなくなってきた。というか油断すると昨日のコードでも意味が分からない。

package ってのがあるらしいなぁと思いつつ、今から数年前に Perl をやり直すまではお世辞にもいいコードは書けなかったし、今も書ける自信はない。(今の状態は去年の日記のまとめにその一端が見て取れると思う。)

とにかく、自分で書いといて意味が分からないコードが増えて、いやな気分を味わうことが増えてきたという印象。Perl そのものの問題ではないことは今なら分かるんだけど、ちょうど Perl をいじっていた頃がそれと重なったのでとにかくいい印象がない。ただし sh, grep, awk のスーパーセットとして目的を達成するという意味においてはやはりかなり強力で、強大だし、今後も Perl のない世界というのはまず想像できない。

ただ PHP と同じで、ユーザーがわざわざアクションを起こして守らなければならないお約束が多すぎる。こういう言語は入門には向かないと思う。気にしなければ気にしないでも動かせるが、最初に悪い作法を身につけたあとに良い作法に切り替えるのは意外と大変である。特に動くコードが出来上がってしまってから切り替えるのは殊の外シンドイ。

ゼロから自分で学ぶのではなく、カッチリしたコード資産と規約のある会社に入って学ぶのならいいと思う。

Ruby 挫折体験と Ruby を薦める理由

Ruby は一度挫折している。その理由はいくつかあって、

  • Ruby に興味を持った時期辺りから自分の興味がテキスト処理やプログラミングからいったん離れてしまった
  • 初めて書籍を買わずに習得しようと試みた
  • オブジェクト指向を意識しすぎた
  • ドキュメントの想定するオブジェクト指向の習得レベルが自分より高かった

この Ruby の学習よりも前だったか後だったか、ちょっと記憶があいまいなんだけど、概念的なレベルではオブジェクト指向は勉強していた。しかしまったくコードを書かずにはやはり理解できないもので、ふーんと思っただけだった。で、この知識だけの状態を Ruby で実体の伴うものとしたかったのだが、はっきり言ってまったく歯が立たなかった。

C++ や Java など他の、教科書の豊富な言語で少しでもオブジェクト指向を味わっていたなら話は違ったのだろうけど、Ruby はそこにあまりに自然にオブジェクトが存在するので、オブジェクト指向以前のノウハウをすでに持っている状態で「よーしオブジェクト指向を勉強するぞ」と意気込んでしまうとまったく空回りしてしまうのだ。というか自分は空回りしてしまった。

たぶん言葉の意味なんかよく分からなくても動かして納得するというプロセスをくり返せばそれでも大丈夫なんだと思う。変な色がついていなければそんなもんなんだと飲み込むことは可能だし、今は書籍を始め様々な情報も充実している。

その後、いったん Ruby を離れ、PHP をやって、その後もう一度 Perl を経たあとに Ruby に触れたのだが、今度は驚くほどスムーズに習得できた。

このとき、いちばん自分の中で引っ掛かっていたのは PHP の class 構文とオブジェクト指向である。どうも納得いかない。これが本当にオブジェクト指向か? この程度のものがパラダイムシフトと呼ばれるものなのか? 全然便利じゃないじゃんと感じ、また挙動が自分の納得できるものでなかったため、Perl で package を使ってオブジェクトを作ってみた。PHP で納得のいかなかったものが Perl で書き直したらしっくり来た。恐らくこの時点ではまだ PHP より Perl の方が自分の中で得意な言語だったんだろうし、Perl のオブジェクト指向は最初から組み込まれている機能ではないので、オブジェクトを構成する要素をバラバラにして直に手に取って見ることができる。これが本当によかった。「あぁ、こういう風にしてオブジェクト指向を実現するんだな」ということが分かった気がした。同時に Perl の書きにくさとモダンな機能のありがたさも実感できた。(このときはイマドキの Class:: 系のモジュールなどは一切使わずに全部ゼロから書いて動かしてみたのでそれなりに苦労したが、この経験は本当に大きかった。)

そして再び Ruby である。Perl であんなに苦労した部分がほとんど最初から解消されている。何もかもオブジェクトだ! そうか、Array がオブジェクトだとメソッドの引数に array のデータを入れる必要がないから引数が減るね! これで引数の順番とかくだらないことを気にしなくていいね! PHP の array_* 関数が多いのをすごいとか思っていたけどバカバカしくなるね! $this-> とか $self-> とか要らないからコードが見やすいね! < だけで継承できてめっちゃ楽だね! ! とか ? がメソッド名に使えると意味がはっきりしていいね! 動的に class の中身が書き換えられると自分のせいじゃないエラーをなくせるからいいね! 同じメソッド名なら別なクラスでもいい感じに似た動作をする ( duck typing ? ) ので、いちいちマニュアル引かなくてもだんだん Ruby の動作が予想できるようになってきていいね! てゆーかやっぱりオレは変数に $ が付くのはきらいなんだ!

本当にこんな感じで感動の連続だった。どうしてこんなにすんなりと自分の中に入ってくるのか不思議なくらい。Perl の書きにくさ読みにくさも PHP の不自由さもない世界。1.8 になってライブラリも増え、本当に楽で便利で、書くことがとても楽しい言語。それが正直な感想だ。そして薦める理由はこれだけではない。

何より、Ruby の哲学は Perl と違って優しくユーザーを拘束してくれる。「これはこうするのがいいんだよ」という使い方をユーザーに提示してくる言語なので、それに従っているだけでそれなりに良い作法が身につく。これは Perl や PHP にはないメリットである。先ほど Perl はゼロから自分で学ぶ場合にはオススメしないと書いたが、同じポイントで Ruby は初学者にオススメできると思う。(もちろん組み込みの class を破壊できるなんてのは悪い作法を身につけやすいとも言えるんだけど。)

例えば、少なくとも自分は Ruby を触ってみるまでは Perl, PHP 3/4 のようにインスタンス変数に直接外からアクセスできることにそれほど大きな疑問を抱いていなかった。アクセスしない方がよいという知識は持っていたが、実際にアクセスできることに対して疑問を抱いていないので、当然のように「アクセスできることを前提としたコード」を書いてしまっていた。これがしかし Ruby ではできない。attr_* でアクセッサを定義してやるとほとんど使用感は変わらないんだけど、「この一手間がなんかイヤ」で、だんだんインスタンス変数にアクセスできないことを前提として書くクセがついてくる。

すると今度は直接アクセスしないことのメリットに気づくようになってくる。例えば PHP では 0 が偽で、0 を返してくる関数と false を返してくる関数が混ざっているので

foo() !== false

って書かないとヤバいよ、みたいなバッドノウハウがあるわけだけど、Ruby の場合は

foo?()

のように ? で終わるメソッドは boolean を返す約束になっていて、ユーザーもこれに従うことを推奨されている。あぁそうか、PHP でも自分でそういう関数を作っていくようにすればいいんだと気づく。だから自分のコードは

if ( $val !== false ) {
}

とか

if ( hoge() !== false ) {
}

とかできるだけ書かなくてよいように、

if ( $obj->is_fuga() ) {
}
if ( $this->moge_exists() ) {
}

などで書けるように気をつけている。

もっとも、「いいコードとは」的な読み物を読むのは昔から好きだったので、ずいぶん前から「すんなり読めるコードを書くべき」というのは知識として持っていたんだけど、それが身になったのは、自分の中では Ruby のおかげである。

毛嫌いしていた JavaScript の面白さ

挙げてなかったけど JavaScript も今はかなり好きな言語の一つである。昔はうざいだけの装飾に使われている、ブラウザごとの差異がでかくて扱いにくいだけの言語もどきと思っていたけど。

残念ながらいろいろ制約があったり、ブラウザ間の挙動の揺れとか気にしなきゃいけない点が多いし、これがあればバリバリ仕事が進む!的なものではないと思うけど、WSH + JScript とか Widget とか JavaScript が使えると便利なシーンは増えているし、「目に見える場所」で使うことが多いので、JavaScript をいじっているときってのはだいたい楽しい。世の中のブラウザがすべて Firefox だったらどんなに楽しいだろうと思う。

まぁ愚痴はともかく、JavaScript の面白さは自分の中では Ruby の面白さとほとんど同じである。一度じっくり EcmaScript として取り組んだことがあるんだけど、このときメソッドと変数の区別がないことに多少面食らった程度でほとんどは Ruby の知識を援用してすんなり理解できた。スコープは確かにちょっと難しいけど、そこまで凝ったものをゼロから自分で書くこともないので、その辺は気にしないで済んでいる。

ちなみに好きな JavaScript フレームワークは prototype.js で、これはたぶん Ruby が好きなことと関係があると思う。Perl 好きな人や Python 好きな人はきっと違うものが好き。

PHPはぁー…どうだろう?

可も不可もあるけど、不可の方が今は目につきやすいです。

PHP を触ったときには Perl が書けていたし CGI も分かってはいたので習得自体はえらく簡単だったと思う。今じゃ当たり前すぎるのかあんまり評価されないけど CGI でも fastcgi でも mod_php でも同じコードが動くのはすごいと思った。エンコーディングの変換とかも便利だし、全体的に扱いやすくていいなと最初のうちは思っていた。

しかし度重なるマイナーアップデートという名の仕様変更の嵐に直面してすっかり印象が悪くなった。そのくせ htmlspecialchars() とか引数の追加でお茶を濁すんじゃなくて仕様を変えるべきじゃねーの、というところには変更は入らずに、PHP ユーザーである我々のコードの修正を余儀なくされるとかいうことが起きる。関数の数が多いのはいいが完全にフラットなのでやたら関数名が長く、しかも全体的に統一感がない。引数の順番はバラバラ。似たような名前の似たような機能の別な関数で引数の順番がひっくり返っているとか、あり得ないトラップがそこら中にある。

機能的には PHP は気が利いているんだと思う。でもデザインがなっちゃいない。あるだけ。アジアンテイストな部屋に北欧インテリアを飾っているようなバランスの悪さを感じる。目的は達成できるが美しくない。そういう印象。それが気にならない人には向いているが自分は正直やめられるもんならやめたい。今さらやめるにはノウハウが溜まりすぎてしまったけれど。

あと意外に困るのは Unix CLI 文化と相性がよくない点。こう言うと CLI バイナリは 4.3 から独立してて普通に使えるよ!ってムキになる人がいるかもしれないし、実際に CLI で活用している人がいるのは知っている。でもはっきり言ってこの CLI バイナリのオプションの取り方とか独特すぎる。じゃあ Windows 対応がいいかというとそうでもなくて、本家に Windows 用バイナリがあるってだけで Windows 版では実装されていない関数が意外にあったり、嘘実装の関数があったりして凹まされました。最新の 5.x 系での状況はよく分かってませんが。(Windows で動かさなくなったので。)

一言で言うと PHP は Perl や Ruby とは異なる意味で節操がない。そして数多くの便利な機能を提供してくれたが、その分多くの自動化、便利機能が Web アプリで足を引っ張ることも教えてくれた。そういう意味では非常に勉強になったとも言える。

ただ、同じ経験を「これからの人」にさせたいとは思えない。確かにブラウザに何かを表示するまでの時間は短いよ。誰かが環境をお膳立てし続けてくれるなら結構快適だ。でも使い続けさせたくないのよ。分かる?この気持ち。

まとめ

自分の場合は Ruby にたどりついて「知識が身になる」経験がとても多かったためか、Ruby の評価はけっこう高い。そして希代の言語ヲタの作った言語だけあっていろんな言語からおいしいとこどりしていることに気づき始めて、「あぁなるほどこの言語にファンがいるのはこういう理由か」なんてことが分かってくる。そういう体験もなかなか面白い。

もっとも、PHP や Perl に対するネガティブな意見の結構な割合は、触らなければいけない稼働中の Perl コード、PHP コードがイケてないことに起因しているとも思う。自分の中で Ruby の評価が高いことにおいて、「Ruby で書いてしまったクソな遺産があんまりない」っていうのは大きいと思う。これが例えば仕事で Ruby をいきなり使わされて、よく分からないけど動くコードをそれなりの数書いてから振り返ったら、Ruby よりも Java の方がよかったとか言ってるんだと思う。

また正直に言うと自分はクロージャとかファーストクラスオブジェクトとかそういうカタカナが好きではないし、ぶっちゃけよく分からない。JavaScript の function がファーストクラスかどうかなんて知らない。ただものすごく便利なものだってことは知ってて、便利なものが好きだってだけ。

だから言語の学習においてこの要素とこの要素がこういう風に入っていて、……とかそういうことは語れないし、語る気はない。こういう経験をしたワタシがこういう風に各言語を捉えています、ということしか書けない。

だから結論とかオチとかはないです。参考になるかどうかも分かりません。けど、何かの足しにでもなるといいなーと思ってます。


2008-02-09 [長年日記]

_ Opensource FastLadder 設置失敗

※ 連休終わりに書いてますが、ネタは休み初日の内容です。

連休の課題第一弾として選んだ FastLadder 設置、見事に玉砕しました。

環境、

  • OS は FreeBSD
  • Ruby は ports で入れた 1.8.6
  • ports の Rails に 2.0 は来てなかったので gem から最新の Rails をインストール

という状態で、ハマったのは FreeImage.

要求されているのが 3.10 で ports で入れてあるのが 3.9.3 だからか、どう頑張ってもちゃんと読み込んでくれない。最初インストールした場所が悪かったのかなと思ってあっちこっちに link 置いてみたけどダメで、なんじゃこれと思って ktrace したら最後の最後、libfreeimage.so.3 が自分自身を指す link になってて file open を諦めていたんだけど、これを直してもダメ。

引き続き ktrace して見てみると、libfreeimage.so.3 -> libfreeimage.a を open してるっぽいんだけど見事にスルーしている。0.0.1 のときは invalid file format ってメッセージが出ていたような気がするけど 0.0.2 にしたらそのメッセージは出なくなったので、もしかしたら気のせいかも。とにかく、libfreeimage を読んでくれない。Ruby の dlopen の問題なのかとかあれこれ思ったけどこの辺の事情にまったく疎いので深追いをやめる。

あと野良で FreeImage 3.10 を入れる手もあったんだけど、面倒くさくなってやめてしまった。これが読み込めないと crawler もまともに動かないし mongrel にアクセスしても 500 しか返ってこない。つまり、まったく使えていない。Linux, Windows, MacOSX ではそれぞれ動かしている人がいる気配なので次は Linux でやってみるか。

FreeBSD では情報が増えてきたらもう一回やってみようかな。どなたか FreeBSD で動いたって人いませんか?


2008-02-11 [長年日記]

_ 言語の学び方続報

自分の前回のエントリは見事に若い人にもそうでない人にもスルーされ、誰も派生エントリを書いてくれずに凹んでいたけど、全然別口で情報が出てきたからいいや。ばーか。

自分はこの二人の足下にも及ばないけれども、原典派と大量の(コードを含めた)情報派の両方の言い分はなんとなく分かる。

前回なぜ自分があえてどれくらい言語の学習に苦労したか、あるいは逆にこの程度ならそこら辺のボンクラでも使えるようになるのかを書いたかというと、単に自分語りをしてみたかったというだけじゃなくて、その一つは 2ch とかで見かける

「言語なんてどれでも使えてこそプロだし、どれでも一緒」という意見にいつも疑問を感じていた

から。なぜなら

世の中には BASIC のような Perl コードや Java コードが実在し、動いている

からだ。この悪夢のようなクソコードがどうも「どれでも一緒」と言ってはばからない人の所業のように見えてならないのよね。ここはまったく根拠レス。理由は聞かないで。あえて言うなら、

有名なハッカーはたいてい得意な言語を持っていて、「どれでも一緒」と言える状態ではないように見えるから。

かな。話がそれましたか?

まぁクソコードの話はともかく、問題は「らしさ」の方。らしさについて今回

  • より多くのコードに触れる
  • 原典を読む

の二つの方法が挙ってきてるわけだけど、方法はともかく

割となんでも習得できるよと言ってはばからない人が「その言語らしさ」や設計思想を持ち出してくれている

ところがポイント。つまり

「どれでも一緒」なんかじゃねぇ!

ってこと。ちゃんとここを意識して、そのらしさ、設計思想を吸収するために自分に適した方法はコレ、と言ってくれている。ここ重要。

そうなのですよ。ぶっちゃけ「文法をやって何か動くものを書くだけ」だったらなんとかなるんですよ。オレが書いても説得力ないけどさ! 問題はその先。Ruby に「挫折した」ときだって Ruby らしくないコードならそりゃ書けてましたよ。でもそれじゃ面白くないし書きにくいだけなわけ。Ruby らしく書けてこその Ruby でしょうよ。それを含めてものすごいスピードで習得できる人が「どれでも一緒」って言うんなら納得するけど、本当にそんな人いるの?という疑問がどうしても残るわけです。

今回、その疑問はますます強くなったというか、「どれでも一緒」という意見は眉唾で聞こうという思いを新たにした次第でございます。

他人の経験を読めるのはとても面白い。だからみんな自分語りしてくれ。自分語りの文脈のときにね。

本日のツッコミ(全2件) [ツッコミを入れる]

_ takano32 [どんな言語が使えますか?みたいな質問には手続き型と関数型は使えます。あと、オブジェクト指向も理解してます。みたいな答..]

_ wtnabe [大枠ですね。関数型使えますって言いたいなぁ。自分でなんとかしないと自分が腐り始めていくのを感じます。]


2008-02-16 [長年日記]

_ IE7キタ

別に楽しみにしていたわけでもないんだけど、なにやらうちの Vista には自動で入っていたらしい*1。で、気がついたんだけど、今までこの日記の h3 の部分とか無造作に border 引いた部分で border が消えちゃうってことが実はよくあったんだけど、それが直ってるみたい。要するに CSS 対応がよくなったみたいだと。

我慢して IE 対応を無視してきた甲斐があったということだね(違

それにしてもメニューバーがデフォルトで表示されないとかメニューバーの位置がアドレスバーの下に来ちゃうとか、なんか自ら Windows アプリのお約束とも言える部分をぶち壊してるけどいいのかな、これ。Windows Live Toolbar は邪魔だから消しておいたけど、この、、、タブの右の方にあるこれは消すか場所動かすかできないんだろうか? タブ増えていったら絶対邪魔になるんだけど。(「ツールバーを固定する」のチェックを外すと「寄せる」ことはできるけど、邪魔なもんは邪魔だ。)

というかタブ開くの遅いなぁ。クイックタブの機能は便利だと思うけど。あ、アクティブでないタブはワンクリックで閉じることができないんだね。親切なのかなぁ、これ。

もうひとつどうでもいいことだけど、アイコンが今となっては寂しいね、IE って。最初グラデーションがついたときは「おぉ!」とか思ったけど、このご時勢に e の周りのラインの色が変わっただけってのはちょっとどーだ。

いいことも書いておこう。CSS 対応は見た目に分からない細かい話なので普通のユーザーは嬉しくない。ユーザーが嬉しいのはやっぱこのズームかな。タブは使い慣れないうちはウィンドウデザインが変だなって感じる程度だと思うけど Opera 風の画像の拡大縮小まで行われるズームは便利。

フィードの機能はもっと勝手に前面に出てこないとあんまり使われないかもしれないなぁ。あ、フィード表示はタブ開いてからやってよ! 編集中のタブの上にそのまま表示しちゃうとビビるからやめて。

というわけでこの日記は IE 7 で書いてみた。そんだけ。

Tags: CSS MS Web IE

*1 よく知らない。ひどい。


2008-02-20 [長年日記]

_ svnリポジトリ内のヤバげなファイルをあぶり出す方法

  • ある程度以上古くて
  • ある程度以上放置されている

ファイルをあぶり出してみた。

以下の2つのスクリプトを組み合わせて、

sh anasvn.sh | ruby warning.rb

みたいにして使う。(スクリプトの名前は適当。)閾値とかファイルの洗い出しルールは自分で勝手に書き換えてください。

読めば分かるけど必要なのは

  • svn
  • find
  • awk
  • ruby

です。CVS ? svk ? git ? RCS ? 現物合わせで適当にしてください。

確認は gawk 3.1.4, Ruby 1.8.4, svn 1.4.4 で行いました。

anasvn.sh

対象ファイル群を洗い出すための find のルールを適当に書き換える。例では

  • 実行したディレクトリ以下で
  • 拡張子 .php のファイル

をダーっと洗い出してチェックします。で、出力は

filename TAB 最初のcommit日時 TAB 変更のあった回数

という形式です。

#! /bin/sh

LANG=C

for i in `find . -name '*.php'`;
do
    svn log -q $i | awk -v filename=$i '
BEGIN {
    sum = 0;
    FS  = "|";
    OFS = "\t";
}

/^r[0-9]+ / {
    sum++;
    time = $3;
}

END {
    # time means first commit datetime
    print filename, time, sum;
}
'
done

warning.rb

TOO_OLD, TOO_FEW を適当に書き換える。

出力は上のものをそのままスルーで出します。

#! /usr/bin/env ruby

require 'time'

FS = "\t"
TOO_OLD = Time.parse( '2006-01-01' )
TOO_FEW = 5

while ( line = gets )
  arr = line.chop.split( FS )
  filename = arr[0]
  date     = Time.parse( arr[1] )
  times    = arr[2].to_i

  if ( date < TOO_OLD and times <= TOO_FEW )
    puts line
  end
end

最終的にはある程度「作った」のが古くてあんまり人の手に触れていないファイルを TSV の形式で抜き出すことができます。

これで出力されるファイルはカタいライブラリか、さもなくば見捨てられている可能性があります。

cron で回すようにすると「この子は可愛そうです、相手してあげてください」アピールになってよいかもしれない。どうだろ。


2008-02-21 [長年日記]

_ CSVを固定長フォーマットのテキストに変換して普通のエディタで開く

文字コードとか考えてません。最初 gawk で書いてたけど match() でマッチした文字列を配列で受け取れるgawk拡張について

3.1.5 はバグ持ちだよ

という記述を見つけてしまって凹んでました。というか手元の 3.1.4 で試して動いたんだか動かないんだかよく分からなくて放り投げてしまいました。awk スクリプトで最新版を要求するなんて優しさが足りない。

Therefore use:

echo test4325363test | gawk 'match($0, "([^0-9]*)([0-9]+)(.*)", a) { print a[2] }'

to extract the number.

Please, note that gawk 3.1.5 has some bugs in the match function. These should be corrected in gawk 3.1.6 (see ftp://ftp.gnu.org).

How to gram awk's regexp submatches? - Object Mix

というわけで Ruby に鞍替え。

なんか諸々 CSV に決め打ちで TSV とか対応できませんけど勘弁してちょ。optparse で delimiter 受け取って動的に正規表現作ればイケますよ、とだけ書いてお茶を濁しておく。

途中、範囲式からループを回す処理は見慣れないと気持ち悪いけど、なぜか Ruby を書いていると i += 1 とかカウンタを自分でいじる方が気持ち悪いと感じるようになるのです。for ( ; ; ) 文がないので while の中で自分でカウンタいじることになるんだけど、そうするとインクリメントのタイミングによって値が変わっちゃうとかそういう落とし穴を作ることになるんでやりたくなくなるのです。do end と { } が混ざっているのはごめんなさいということで。

#! /usr/bin/env ruby

class Csv2Fixed
  def initialize
    @num_fields = 0
    @num_lines  = 0
    @len_cols   = []
    @buf        = []
  end

  def run
    while ( line = gets )
      arr = split_csv( line.chomp )
      if ( arr.size > @num_fields )
        @num_fields = arr.size
      end
      store_line( arr )
      @num_lines += 1
    end

    output()
  end

  def store_line( arr )
    @buf[@num_lines] = []

    (0 ... arr.size).each do |i|
      str = arr[i]
      if ( @len_cols[i].nil? or str.size > @len_cols[i] )
        @len_cols[i] = str.size
      end
      @buf[@num_lines].push( str )
    end
  end

  def output
    (0 ... @num_lines).each do |i|
      arr = @buf[i]
      (0 ... arr.size).each do |j|
        printf( "%-*s ", @len_cols[j], @buf[i][j] )
      end
      printf( "\n" )
    end
  end

  def split_csv( str )
    str = str + ","
    arr = []

    str.scan( /(\"(?:[^\"]|\"\")*\"|[^,]*),/ ).each { |e|
      arr.push( e.to_s.sub( /^"/, '' ).sub( /"$/, '' ).sub( /""/, '"' ) )
    }

    return arr
  end
end # of class Csv2Fixed

if ( __FILE__ == $0 )
  app = Csv2Fixed.new()
  app.run()
end
Tags: Ruby

2008-02-27 [長年日記]

_ 生存報告

今年に入って日記の更新頻度がガタ落ちしてますが、別にバーンアウトしたわけではないし、Twitter の方に細かい思いつきや文句を吐き出せて満足しているのもあるけどそれだけでもないです。

いちばんの理由はある程度以上の長さの日本語を書く気力すら残らないほどヤラレているからです。

Fastladder はいつも pin 留めが 100 を越えてから本文を読み始める始末だし、ブックマークの方もタグすらつけてないものが多くなってきていよいよ訳が分からなくなりそう。*1まぁ本文を読んでないだけでフィードに目を通す時間はまだ確保できてる分マシなのかもしれないけど。年末から Amazon で頼んだけど配送予定が立たず、リアル店舗を探し回ってまでゲットした本が何冊もあるのに全然読めてませんT_T

まだしばらくはこんな感じかなぁ。ま、身体も心も病んでないのが幸い。単に体力がなくて無理が効かないだけとも言う。世の中にゃこれに加えて毎日通勤地獄に耐えている人がゴマンと居るわけだから恐れ入るよね、まったく。

Tags: 日々

*1 最近やっと気づいたけど、自分の場合は調べもの以外では読む時間がない方がブックマークは多くなる傾向があるらしい。


2008-02-29 [長年日記]

_ Rswatch なんてものを書いてみました

※ 実際には3/1なのにあえて4年に一度の肉の日にぶつけて書くメソッド :)

Swatch あるじゃないですか。兄さん、時計じゃないっすよ。そんなベタなボケは要らないっすよ。ログ監視ツールっすよ。

SWATCH: The Simple WATCHer of Logfiles

でまぁこれは皆さんご存知の通り Perl で書かれてるわけっすよ。

昔からあるツールで非常に有名なわけですけど、例によって Perl なので(?)依存モジュールがいくつかあって、まぁ CPAN で入れればいいのは分かってるけど、CPAN モジュールってシステム管理的には超邪魔くさくね?といつも思ってる自分としては OS 全体のパッケージ管理システムに入ってないものは入れたくないわけっすよ。でも監視はしたいんす。ずるずる先延ばしにしてきたんすけど、決心して

Ruby で似たようなもの書けばいいじゃん

ということで書きました。

http://svn.coderepos.org/share/lang/ruby/misc/rswatch.rb

やっと動くものを CodeRepos にうpできたよ! 車輪の再発明上等!

で、ドキュメントもサンプルも何もないんでここに下書きを書いておきます。

必要なもの

  • Ruby と Ruby の知識
  • File::Tail モジュール http://file-tail.rubyforge.org/
    • 結局依存モジュールはあるわけですけど、pure Ruby 一個なのでご勘弁を。
  • 監視したいログに関する設定を Ruby か YAML で書く
  • 監視に利用するルールとアラート対象のログを見つけたときのアクションを Ruby で書く

意外にハードルが高くなってしまいました。これはひとえに

中身がまだ何もない

からです。

起動方法

Usage:
  rswatch.rb [options]

Options:
  --dry-run      -n  parse config, but no action
  --lib-file     -l  LIBFILE
  --config-file  -f  CONFIGFILE
  --config-yaml  -y  YAMLFILE
  --daemon       -d  daemon mode
  --debug        -D
  --help         -h
  --version      -v

設定を -f で Ruby の文法のファイルか、-y で YAML のファイルで与えます。-l で与えるライブラリがキモなんですがこれは後で触れます。

動作する Ruby のバージョンと File::Tail の置き方

Ruby 1.6.8 と 1.8.6 で動作確認しています。これを書いたいちばんの理由は Swatch はパッケージに入ってないけど Ruby 1.6 なら入ってるよ、という環境でどうにか監視したかったからなので、1.6 で動かない書き方は不許可、という枷を自分に課しています。そうです、あえての GetoptLong です。

File::Tail は gem でインストールできますが、アーカイブを取ってきて $LOAD_PATH の中で file/tail の階層になる場所に置いておけば、gem のない環境でも大丈夫です。一応 rswatch.rb そのものの置かれているディレクトリにもパスを通すようにしてあるので、

rswatch.rb
file/
     tail.rb

という形で置いてくれれば動きます。

設定の書き方

こういう構造の Hash を与えます。

# -*- ruby -*-

{
  # recipe name
  'simpleecho' => {
#    'disabled'   => true,
    'watchfiles' => [ '/var/log/apache22/httpd-access.log',
                      '/var/log/apache22/httpd-error.log',
                      '/var/log/messages',
                    ],
    'rules'      => 'nonzero?',
    'backline'   => 1,
    'action'     => 'echo',
    'max_repeat' => 3,
#    'ignore_first' => false,
  },
  RECIPENAME => {
  }
}

監視対象群のひとかたまりをレシピと呼びます。レシピには何をしたいのか分かりやすい、適当な名前をつけてやってください。中身は

disabled このレシピを無効とするかどうか
watchfiles 監視対象ファイル群
rules 監視ルールを1つ以上。複数ある場合はANDでチェックします。
backline 一度に参照できるログの行数。デフォルトは 1
action ルールが成り立った場合に実行するメソッド
max_repeat 上の action を何回まで実行するか。デフォルトは無制限。
ignore_first起動後最初にルールにマッチするログを見つけてもそれを無視するかどうか

ライブラリを用意してください

今のところ rule も action もお飾りのものしかありません。申し訳ありませんが

# -*- ruby -*-

module Rswatch
  class Rule < RuleBase
    #
    # [Param] Array backlog
    #
    def example( backlog )
    end
  end

  class Action < ActionBase
    #
    # [Param] String logname
    # [Param] Array  backlog
    #
    def example( logname, backlog )
    end
  end
end

こんな Ruby のファイルを用意して、この中で自由に書いてください。

rules, action に指定できる文字列はここで定義されたメソッド名になります。

で、起動時の -l オプションでこのファイルを指定してやります。backlog は backline で指定した行数分のログの中身が Array で渡ってきます。//m =~ backlog.join で一刀両断にするもよし、好きに料理してください。

残念ながらログファイルの解析に使える道具は Ruby 標準のものしかありません。Apache のログをサクっと解析したいとか syslog を手早く解析したいとかそういうことをしたい人は自分でなんとかしてください。

悩んでいるところ

ignore_first の値

ignore_first の値は変かもです。例えばトラブル復旧後に再起動したとかいう場合、最後のログがルールにマッチするものになっていることがあります。すると復旧後すぐ警告が発生する可能性があります。今のところデフォルトでこの値を true にしてあるので回避できますが、これは false にしておいて監視対象のログの種類によってユーザーにスイッチしてもらった方がいいような気もしています。

テスト段階ではログのほとんどがルールにマッチしちゃう状態になっていたので true にしていましたが、今考えると false が正しい気もします。ご意見ある方ツッコミください。

action の与え方

action の与え方は二つの点で悩んでいます。

  1. 複数の action を与えられるようにすべきか?
  2. いっそ Ruby のブロックを与えられるようにしようか?

特に二番目の点で悩んでいます。と言うのも、個人的に swatch では awk 風の書式の中で決まった処理しか書けないのが不便だなと感じているからです。exec や pipe を使って外部のプログラムは呼べますが、やはり不自由な感じは否めません。だったら Ruby でそのまま書けた方がいんじゃね? 設定ファイルで action に Ruby のブロックをそのまま書ければいいじゃんとも思うのですが、そうなると逆によくある処理を名前だけで呼び出すという方法と同居させにくくなりますし、YAML で設定を書くのが難しくなります。*1

あ、String だったら Ruby コード、Symbol だったら action名、という分け方はありかも。確か Ruby の Syck は Symbol も解釈できたはず。

Rubyist Magazine - プログラマーのための YAML 入門 (中級編)

  • データ型について
    • 真偽値と解釈されるデータは、"true/false", "yes/no" のほかに、"on/off" があります。また大文字でも CamelCase(先頭だけが大文字)でも認識されます
    • YAMLの仕様にはないですが、Syck では ":foo" のような「コロン+単語」の組み合わせは Ruby の Symbol と解釈されます。

やっぱそうだ。

Symbol って便利なんだなぁ。問題はブロックを渡された本体側でそれをどう取り扱ったらいいのかまだ何も考えていないところか。

目標

ゆくゆくはそうですね、ライブラリを目的別にチョイスしてサクッと監視できるようにしたいですよね。そのために本体から rule も action も切り離したんだから。ほんとによく使う基本のものだけ Base の方に取り込んで行く方向にしたいです。

あとあれですね。テストキット欲しいですよね。自分の書いた rule や action が正しく機能するかどうか簡単にテストできるようにしたい。どうすればいいかな。適当なログを吐くコードを書けばいいのか。むーん。

謝辞と愚痴

今回初めて Thread と fork を使いました。今までデータの加工しかしてなかったんだなーと痛感。以下の情報が参考になりました。

最初「スレッドはメモリを共有する」という言葉の意味が分からず悩みました。プロセスの場合は完全に分離してしまうがスレッドの場合は同じプロセス内なので flock みたいに行儀よく扱えば別々のスレッドで同じ変数を「扱うこともできるよ」という意味だと気づくまで時間が掛かりました。今回は共有したいものは何もないので「気にすることなかったのか!」と気づいてガックリ。しかしそのあとメインスレッドじゃないところのエラーがすぐに見つからないという事態に気づくまで妙なハマり方をすることに。

fork と Process.setsid の簡単なサンプルが見つからず、見当違いなコードを書いて悩むことしばし。それぞれの存在は知っていてもどこに何を書けばいいのかが短く的確に書かれているサンプルのなさに腹が立つと同時に、自分が Unix のプロセスの概念がよく分かっていないことがバレバレに。tty の detach ってそういう意味かーと初めて知った。*2

最終的にはスレッドも fork も明るいところでクックブックを読んだらだいぶ理解が進みました。電気を消してネットだけに頼っていたらハマった。

*1 YAML にこだわる意味はないという考え方もあると思いますが、Ruby コードを書くのが目的ではなく監視が目的であるという場合は、やはり設定ファイルが生の Ruby コードであるというだけで十分敬遠されると思うので、できれば生の Ruby コードでしか設定が書けないという事態は避けたいです。というか生の Ruby コードで設定を書けるようにしてあるのは 1.6 でも動くようにするためです。

*2 今回 stdin, stdout, stderr はあえてつぶしてありません。