Recently in Perl Category
台風の動向が気になります。
そういえば、お盆に血迷ってメーリングリスト配送システムを作ってしまったので、
GoogleCodeで公開します。
名前はmaidodomoです。
http://code.google.com/p/maidodomo/
全部Perlで書かれており、aliasesを使うMTAであれば使えます。
できることは、
- 当然ですが、メーリングリストを作れます
- 言うまでもありませんが、メーリングリストのメンバーにメールを配送します
- 当たり前すぎですが、件名にメーリングリスト名と通し番号を付与します
気が向いたら、気になるところをテコ入れします。
言い忘れてましたが、ライセンスはApacheLicense2.0です。
使い方とサンプルは下記の通り。
■使い方
split /PATTERN/, EXPR, LIMIT
split /PATTERN/, EXPR
split /PATTERN/
split
$var_before = "Sato:Suzuki:Kimura";
@list = split(/:/, $var_before);
@listの内容は、下のようになっています。※Data::Dumperを使用した出力。
$VAR1 = 'Sato'; $VAR2 = 'Suzuki'; $VAR3 = 'Kimura';
そして、この逆の処理をしてくれるのが、join関数です。
$var_before = "Sato:Suzuki:Kimura";
@list = split(/:/, $var_before);
$var_after = join(":", @list);
$var_afterの内容は、下のようになって$var_beforeと同じになりました。
$VAR1 = 'Sato:Suzuki:Kimura';
でも、splitしてjoinして元に戻すのは、はたしてこの方法で良いのでしょうか?
プログラムを書く際に処理をタイムアウトさせたい場合というのはよくあると思います。
そんなときに便利なのが、alarmです。
今回は、Perlプログラムでalarmを使ってタイムアウト処理を実装したいと思います。
alarmは対象の処理が指定した時間(秒)たっても終了しない場合、SIGALRMシグナルを発生させます。
例として、ユーザからの入力を30秒でタイムアウトさせるプログラムを書いてみます。
use strict;
# タイムアウトするまでの時間(秒数)
my $TIMEOUT = 30;
print "What your name??: ";
alarm($TIMEOUT);
my $name =
my $timeleft = alarm(0);
print "Hello! $name";
alarmで囲った部分、ユーザからの入力待つ部分がタイムアウト処理の対象になります。
ユーザが30秒以上入力しない場合、SIGALRMシグナルが発生しプログラムが終了します。
実際に30秒放置しておくと以下のような画面になります。
[hiroki@capybara]~% ./sig_alarm.pl What your name??: zsh: alarm ./sig_alarm.pl [hiroki@capybara]~%
このままでもプログラムを終了させることはできるのですが、SIGALRM発生後に後処理を行いたいということが多いと思います。
# このプログラムでは特にこのままでも問題ないのですが...
上記のプログラムを改良しタイムアウトした際にメッセージを表示するようにしたいと思います。
evalとdieを組み合わせることで後処理を実装することができます。
下記が実装例です。
use strict;
my $TIMEOUT = 30;
print "What your name??: ";
my $name = '';
eval {
local $SIG{ALRM} = sub {die};
alarm($TIMEOUT);
$name =
my $timeleft = alarm(0);
};
if ($@) {
# タイムアウト
print "\nERROR: TIMEOUT\n";
}else{
# 正常終了
print "Hello! $name";
}
まず、タイムアウト処理をしたい対象をevalで囲んでdieしたときでもプログラム自体を終了しないようにします。
dieなどの割り込みが発生した場合には、$@にエラーメッセージが代入されるのでそれによって処理がタイムアウトしたかを判定しています。
eval内では、SIGALRMシグナルが発生したときでもプログラムが終了しないようにSIGALRMのシグナルハンドラを設定しています。
シグナルハンドラには関数のリファレンスを渡す必要があります。
上記例では、SIGALRMが発生した際にはdieするようにしています。
シグナルハンドラはlocalをつけて指定することで、スコープ内で発生した該当のシグナルだけつかまえることができます。
localを指定していない場合、プログラム全体の該当のシグナルハンドルを指定することになります。
上記のプログラムを実際にタイムアウトさせると下記のようになります。
What your name??:
ERROR: TIMEOUT
[hiroki@capybara]~%
今度はシェルからのメッセージではなく、きちんと自分自身のプログラムからのメッセージが表示されているのがわかると思います。
最後にalarmとsleepを併用することは推奨されません。
sleepがalarmを使って実装されていることが多く、正しく動かないことがあるからです。
alarmとevalとdieを組み合わせることでタイムアウトがないような処理にもタイムアウトが実装できるようになります。