動的IPでもうまくやっていけそうなのと、IIJの回線が飽和状態なので、ぷららのダブルルートサービスを解約。
単にBASIC認証のURLにGETリクエストを送れば更新できるようだ。wget等を使えば簡単に書けそう。
ウェブサーバー機(debian)でMTAの設定をしようと調べてみると、sendmailやpostfixではなくEximというMTAがインストールされていた。検索してみたところ、マイナーなMTAらしく、日本語の解説サイトはそれほど多くないようだ。sendmailと同じようなインターフェイスで使えるようで、/usr/sbin/sendmailもeximへのシンボリックリンクになっていた。sendmailなどよりも設定が簡単らしい。
試しにtelnetでlocalhostに繋いだり、sendmailを使うCGIからメールを送ってみたところちゃんと送信できた。
メールサーバーとしての運用はまだだけど、メール送信機能なら使える。メール送信機能を使ったCGIを作りたかったんだよなぁ。
debianで。viで日本語の表示ができなかったので、jvimをインストール。
apt-get install jvim-canna
/usr/bin/vi は /etc/alternatives/vi へのシンボリックリンク、/etc/alternatives/vi は /usr/bin/nvi へのシンボリックリンクになっていた。alternativesは、同じ機能をもった複数のプログラムをインストールしておき、場合によって切り替えて使うための機能らしい。設定は update-alternatives で行うとのこと。
update-alternatives --display vi
で現在の設定を表示、
update-alternatives --config vi
で設定変更。ダイアログが表示されるので jvim を選択。
VineLinuxの場合はもともと jvim がインストールされていた。
”ネットワーク用漢字コード変換フィルタ”nkf をインストール。
apt-get install nkf
ADSL接続のIPが度々変わってしまうようなので、ZoneEditのDNS情報を更新できるddclientというプログラムをインストールしてみた。
wget http://members.rogers.com/ddclient/pub/ddclient.tar.gz tar xvfz ddclient.tar.gz cd ddclient-3.6.3/ su cp ddclient /usr/sbin/ cp sample-etc_ddclient.conf /etc/ddclient.conf cp sample-etc_ppp_ip-up.local /etc/ppp/ip-up.local
/etc/ddclient.confの以下の個所を編集:
## ## ZoneEdit (zoneedit.com) ## server=www.zoneedit.com, \ protocol=zoneedit1, \ login=your-zoneedit-login, \ password=your-zoneedit-password \ your.any.domain,your-2nd.any.dom
ZoneEditのIDとパスワードを設定して、your.any.domain,your-2nd.any.domの個所にIPを更新したいホスト名をカンマで区切って記述。
pppoe接続が確立したときにip-up.localが呼ばれるので、daemonとして起動しておくよりもスマートだ。
Windows 2000のコマンドプロンプトで、UNIX系OSのbashのようにtabキーでファイル名を補完してくれる機能。
レジストリの
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar
のDWORD値を9にする。
などのときに、自分の環境でだけそのドメイン名でサーバーにアクセスする方法:
hostsという、IPアドレスとホスト名の対応が書かれているファイルを書き換える。
たとえば、mydomain.comというドメインを取得し、レンタルサーバーも借りたが今は自宅サーバーで運用していて、一時的にレンタルサーバーへmydomain.comというURLでアクセスしたいときは、hostsファイルに次のように追加する:
レンタルサーバーのIPアドレス mydomain.com
これで、一時的にmydomain.comのホスト名でレンタルサーバーへアクセスできるようになる。用が済めばその行を削除する。
hostsファイルは、Windowsの場合はWindowsディレクトリ以下を検索すれば見つかる。
同じURLで、ブラウザのUser-Agentにより自動的に適切なページを表示する方法。mod_rewriteを使用。
次のようなURL構造であるとする。
uttsu.com/
games/
docs/
ここで、各ディレクトリごとに(必要ならば)携帯端末用のHTMLファイルを置くディレクトリを作る。そのディレクトリで表示する携帯端末用のファイルが1つだけなら、index_m.htmlのようなファイルを用意してもよい。
uttsu.com/
index_m.html
games/
m/
docs/
m/
各ディレクトリ(uttsu.com/, games/, docs/)の.htaccessに次のように記述:
RewriteEngine on
# set mobile
RewriteCond %{HTTP_USER_AGENT} ^DoCoMo/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^J-PHONE/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^UP.Browser/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^KDDI-.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^ASTEL/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^PDXGW/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^L-mode/.*
RewriteRule .* - [E=HTTP_MOBILE:true]
# for mobile
RewriteCond %{ENV:HTTP_MOBILE} ^true$
RewriteRule !^m/ - [C]
RewriteRule (.*) m/$1
index_m.htmlのようなファイルを用意した場合には、次のように記述:
# set mobile
RewriteCond %{HTTP_USER_AGENT} ^DoCoMo/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^J-PHONE/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^UP.Browser/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^KDDI-.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^ASTEL/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^PDXGW/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^L-mode/.*
RewriteRule .* - [E=HTTP_MOBILE:true]
# for mobile
RewriteCond %{ENV:HTTP_MOBILE} ^true$
RewriteRule ^$ index_m.html [L]
これで、同じURLで、アクセスしてきたUser-Agentにより適切なページが表示される。
URLに.htmlという文字を出したくない場合(uttsu.com/games/shooting などのようなURLを使いたい場合)は、次のように記述(ただし、携帯端末のみ。PCはまた別途設定):
RewriteEngine on
# set mobile
RewriteCond %{HTTP_USER_AGENT} ^DoCoMo/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^J-PHONE/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^UP.Browser/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^KDDI-.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^ASTEL/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^PDXGW/.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^L-mode/.*
RewriteRule .* - [E=HTTP_MOBILE:true]
# for mobile
RewriteCond %{ENV:HTTP_MOBILE} ^true$
RewriteRule ^$ m/
RewriteCond %{ENV:HTTP_MOBILE} ^true$
RewriteRule !^m/ - [C]
RewriteRule (.*) m/$1.html
mod_rewriteでURLを内部的に書き換える処理は、サーバー内で無限ループに陥りやすくて危険です。テストはローカルの環境で。
Perlにも少し慣れてきたので、気に入っている点と不満な点を書いてみよう。まず、気に入っている点から。
何と言ってもこれ。単に正規表現が使えるというだけではなくて、正規表現が使いやすいように言語自体が作られているということ。デフォルト変数という変数の記述の省略法と合わせて使えば、短いコードでかなり複雑な処理も記述できる。正規表現はとくにテキスト処理で有用で、標準で正規表現が使えない言語と比べるとそのあたりのコードの量がかなり違ってくる。
UNIX系OSをはじめ、Windowsやその他のOSでもPerlの実行環境を用意すれば、同じコードがそのまま使えることが多い(機種に依存したことをしていない場合)。PerlはもともとUNIX系OSとともに発展してきたという経緯があるので、UNIX系OS(ウェブサーバーでよく使われる)でほぼ確実に使え、CGIの記述用にもサーバーにインストールされている言語を気にしないで気軽に使用することができる。PHPやRubyなどの新しい言語も最近では多くのサーバーでインストールされていることが多いけれど、まだそういう点ではPerlに少し長があるでしょう。
また、Windows版のPerl(ActivePerl)の移植度が高いのもよい。ファイルロック関数などOSレベルでサポートされていない機能もあったりするけれど、だいたいはUNIX系OSと同じコードがそのまま使える。
Javaのような割と厳格な記述しか許されていない言語に比べて、Perlでは同一の処理をするのにかなり多様な記述法ができる。デフォルト変数という、変数の記述を省略したときに暗黙で採用される変数の機能を使えばさらに短く記述でき、冗長性を省いた記述もできる。この点は、厳格な記述法にも多くの利点があるのでどちらがよいとは言えないけれど、ちょっとしたプログラムを書く場合には短く書けるのは便利だ。それなりにまとまったプログラムの場合でも、(好みの問題だけど)適切に短いコードを書くことによってコードの可読性が上がる場合があるように思う。たとえばJavaの場合はif文の条件式ではbool値しか使えない(たしか)ので、(my_valueがBoolean型以外の場合)
if (my_value)
と書くことはできず、
if (my_value != 0)
などと明示的に書いてやる必要がある。Perlの場合は式の値を暗黙のうちに真・偽にして判断してくれ、さらに配列などの場合は要素数が評価の対象になるので、
if (@items)
のような記述もできる。また、
open(IN, 'myfile.dat') or die;
などのような記述は簡潔で便利。
上にも書いたけれど、これは好みの問題で、しかも厳格な型チェックを行ってくれると発見しにくいバグを予防できるので、大規模なプログラム、機械制御などの厳密な動作(記述)が求められるプログラムなどでは厳格な型チェックの方が適しているように思う。
複雑なデータ構造を変数で扱うとき、C/C++やJavaなどでは最初に適切なクラスを用意し、メモリを確保してやるなどの準備が必要だけど、Perlの場合には自動的に判断され、配列への要素の追加などが簡単に記述できる。たとえば、
my %record;
push(@{$record{'男'}{'長水路'}{'自由形'}{'100m'}}, ['山田 太郎', '59.23']);
というコードでは、最初にハッシュ型変数recordを宣言し、次の行でいきなり複雑なデータ構造が用意されているような感じで代入(配列への追加)がされている。かなり読みにくいコードだけど、次のようなデータ構造ができている。()内は取りうる値。
record
sex (男, 女)
course (短水路, 長水路)
style (自由形, 背泳ぎ, 平泳ぎ, ...)
distance (50m, 100m, ...)
[name, time],
[name, time],
...
これを参照する場合は、たとえば
print ${$record{'男'}{'長水路'}{'平泳ぎ'}{'200m'}}[0][0];
とすれば一人めのnameが表示される(あってるかな?)。{}の中には変数が使え、要素数も簡単に取得できるので、実際にはループで回して全体を処理できる。
複数行のリテラル文字列を記述するときに、ヒアドキュメントを使えば簡潔に記述でき、さらにダブルクォーテーションやクォーテーションの記号をエスケープしなくてもよい。
my $body = <<'END'; 1行目 2行目 ... END
実行時に文字列をPerlの文として評価(実行)するという関数 eval が用意されている。これを使えば、できそうでないと思ったことができたり、普通に書けば複雑になりそうなことが簡潔に書けたりすることもある。ある意味とても強力(強力すぎるほど)な機能。ただ、多くの場合、やっている処理はかなりトリッキーなものになる。
それほど重要ではないかもしれないけれど、文字列中に変数の値を展開できるので、コードが読みやすくなったり、記述が簡潔になったりすることがある。とくに、ヒアドキュメントと組み合わせると簡潔に記述できることがある。
my $html = <<END;
<title>$title</title>
<h1>$title</h1>
<div align=right>更新日:$date</div>
${\join("<br>\n", @body)}
END
最後の ${\join("<br>\n", @body)} というのは、配列変数を文字列中に展開するテクニックで、@bodyの各要素を <br>\n で繋げて代入している。
日本語などのマルチバイト文字をPerlで処理する場合、マルチバイト文字内のバイトにエスケープコードが含まれていたりすると正しく処理できない問題がある。Shift JISでもEUCでもこのような問題はある。マルチバイト文字を組み込みレベルで文字単位で扱えない、というくらいならまだよいのだけど、プログラムが正しく解釈されなくて、コンパイルエラーや実行時エラーになるのはかなり不便。
これはPerlの問題というよりも、僕個人の問題という気もするけれど。
Perlは昔からある言語で、オブジェクト指向の機能も途中から追加されたものであるため、最初から設計されている言語よりも記述法の点で幾分スマートでない点があると思う。Perlの本を何冊か読んでみたけれど、オブジェクト指向の使い方についてはいまいち分からなかった。理解力が足りないのか、読んだ本がまずかったのか…。Javaなどのときにはクラスの作り方・使い方などは直感的に理解できるものだったのだけど…。
Perlでのオブジェクト指向、要勉強。
Javaのクラス内のクラスのような感じでサブルーチン内のサブルーチンを記述することができない。テクニックを使えば、それらしいことは一応できるのだけど、そういう機能をもった仕様の言語に比べてはきれいじゃない記述になってしまう。サブルーチン内のサブルーチンは、たとえば次のように記述する:
sub main {
my $val = 2;
my $innersub = sub {
return $val * $val;
};
print "$val の2乗は", $innersub->(), "です。\n";
}
トップダウン式の記述法で記述する場合、内部のサブルーチンはできれば後ろのほうに書きたいけれど、呼び出すときに $innersub が未定義だとエラーになってしまう。
CGIとしてPerlを使った場合、アクセスがあるごとにPerlを起動するのでそのオーバーヘッドがある(普通の用途なら気になるほどではないけれど)。PerlをApacheの組み込みモジュールとして、常時起動しておくという方法もあるようだけど、その場合には、変数の初期化がされていない?などの(小さな)問題もあるという記述を見かけたりするので、今のところ使う気にはならないなぁ。ただ、オーバーヘッドはPerlだけの問題ではないけれど。
PHPなどの場合はもっとスマートな処理がされているのかな。
しばらく使ってみて思ったのは、やっぱり大規模だったり、厳密な記述がしたい場合にはJavaなどの方がしっくりきそうだなということ。ただ、普段の事務処理的な用途やCGIなどなら、わりと高度なことが簡単に記述できるPerlは強力。とくに正規表現が手軽に使えるのは強力で、ちょっとしたテキスト用のフィルタならちょこちょこっと書いてすぐに使える。
PerlでGUIなプログラムを作るためのTkライブラリは使ったことがない。GUIというと、これもやっぱりJavaなどで書く方がしっくりくる気がするなぁ。
個人的には、純粋なオブジェクト指向言語(Javaなど)とこういう手軽なスクリプト言語の2種類は必要だなぁ。
あまり本格的に使っているわけではないけれど、現時点で思っていること。
Perlと同じような感じで、要素の追加などが行える。JavaScriptの場合はあまりよく試していないけど、Perlでは上記のように一気に深い階層への代入ができるので、Perlの方が強力かな。
Perlと同じで、JavaScriptのeval関数も強力そう。ただ、個人的にはほとんど使ったことがない。
JavaScriptでメンバ関数を書く場合、
function myClass() {
...
var myFunc = function() {
...
};
}
とすると、インスタンスごとの関数になってしまう。たとえば、
var a = new myClass();
var b = new myClass();
b.myFunc = function() { return false; };
とすると、a.myFuncとb.myFuncはそれぞれ別のものになる(正確には、最初から違う実体のものだけど、内容は同じだった)。これは、インスタンスごとに異なる関数を持つことができ、実行時にも変更できるので使い道はありそうだけど、インスタンスを大量に作る場合にはメモリを無駄に消費してしまう。
メンバ関数を書く場合には次のようにする:
function myClass() {
...
}
myClass.prototype.myFunc = function() {
...
};
これで、myClassのインスタンスが個別の関数myFuncを持たない場合にはprototypeで指定したmyFuncが使われることになる。
ただ、好みの問題かもしれないけれど、Javaのようにクラス内部にメンバ関数を書きたいな…。
処理に時間がかかるプログラムだったりすると、Internet Explorerの場合には「このまま処理を続けますか?」というようなプロンプトが表示される。処理速度もそれほど速くない(?)ような感じがするので、やはり大規模なプログラムには向いていないのかなぁ。
ブラウザでの実行を主な目的としている(?)ので、当たり前なのだけど、他のプログラムと連携させて使うというようなことができにくい。Javaアプレットとなら少しは連携させられるようだけど。
これは、個人的な使用の場合には自分が使うブラウザを限定してしまえばよい話。しかし、公開用のウェブページでJavaScriptを使う場合(この用途がほとんどだと思う)では、このあたりが大きな問題になる。最近のブラウザではかなり解消されているのかな?(最近はIEでしかテストしなくなった)。
ポップアップ広告などを除去する等の機能をもったProxyソフトをユーザーが使っていると、HTML中のJavaScriptのコードが書き換えられてブラウザに渡されることになり、結果的に思い通りの処理がされないことになってしまう。
ただ、このあたりまで気にしていては何もできないなぁ…。
ページ内のリンク一覧を表示するJavaScript。アドレスバーに貼り付ければ動きます。(動作確認:IE 6.0)
内容:
「お気に入り」に登録すると簡単に実行できます:
javascript:a='';h=location;h=h.protocol+'//'+h.host;l=[];s=[];for(i=0;i<document.links.length;i++)l[i]=document.links[i];for(i=0;i<l.length;i++){j=l.sort()[i];if(s[j.href])continue;else s[j.href]=1;b=j.href.indexOf(h)==0;a+='<style>li{font-size:100%;line-height:110%;}</style><li>'+(b?'<b>':'')+j.innerText+'<br><a href='+j+'>'+j+'</a>'+(b?'</b>':'');}document.body.insertAdjacentHTML('afterBegin','<ul>'+a+'</ul>');eval();
NetscapeなどではinnerTextとなっているところをtextに変えれば動くかも。
オライリーから出版の『プログラミングPerl Volume 1』と『Perlクックブック』を通読。
WebDoc 1.22 公開。
WebNote 1.28 公開。
日記のURLを、.htmlをつけないものにしてみた。
Perlプロファイラのdprofpp(Perlに付属)を使えば、各サブルーチン毎の実行時間などを知ることができる。(『プログラミングPerl Volume 1』オライリー・ジャパンより)
使い方:
dprofpp -p yourfile.pl
WebNoteを実行(index.cgiに引数を指定しないで実行。最新の記事を表示するモード)してみた結果:
Total Elapsed Time = 2.533256 Seconds User+System Time = 1.531256 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 81.7 1.252 1.282 1 1.2520 1.2817 main::do_latest 9.14 0.140 0.340 9 0.0156 0.0378 main::get_file_path 1.31 0.020 0.020 1 0.0200 0.0200 main::put_topics 0.65 0.010 0.010 2 0.0050 0.0050 main::put_log_link 0.00 0.000 -0.000 1 0.0000 - main::BEGIN 0.00 0.000 -0.000 1 0.0000 - strict::import 0.00 0.000 -0.000 1 0.0000 - strict::bits 0.00 0.000 1.422 1 0.0000 1.4216 main::main 0.00 0.000 -0.000 1 0.0000 - main::put_header 0.00 0.000 -0.000 3 0.0000 - main::put_day 0.00 0.000 -0.000 3 0.0000 - main::text_to_html 0.00 0.000 -0.000 17 0.0000 - main::inline 0.00 0.000 -0.000 17 0.0000 - main::escape 0.00 0.000 -0.000 3 0.0000 - main::put_day_toc 0.00 0.000 -0.000 1 0.0000 - main::put_footer
実行するとtmon.outというファイルが出力される。このファイルはperlデバッガ(
perl -d:DProf)によっても出力されるもので、Perlプロファイラはこれを処理して表示するもの。dprofpp単体でも-pオプションでスクリプトファイルを指定すれば実行可。
dprofppの起動オプション:
最近円が強い(ドルが弱い?)ですね。gTLDのドメインを新たに2つ取得したのですが、海外のレジストラで購入してドルでの支払いなので、外国のものを買うときには安くてうれしいです。gTLDならもともと安いものですが、レンタルサーバーも借りたので、こっちは為替によって結構違ってきます。
年間契約ですが更新は自由にできるので、円高の時期に更新すると得かも。
ぷららのダブルルートサービスで使っているIIJ4Uの茨城のトラフィックがすごいことに…。pingを打つとパケット損失が20〜60%程も出る状況。
ぷららの回線で接続すると、なぜかパケット損失は1、2%出るものの、快適に繋がる。
ダブルルートの固定IPでのサーバー運用はやめて、また動的IPでの運用に戻そうかな。でも、学校などのDNSはキャッシュが長くて、IP変更後は繋がらなくなるんだよなぁ。