Perl: July 2009 Archives

ご存じのとおり、Perlのsplit関数は、指定したセパレーターで文字列を分割してリストにします。
使い方とサンプルは下記の通り。

■使い方
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秒でタイムアウトさせるプログラムを書いてみます。

#!/usr/bin/perl

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を組み合わせることで後処理を実装することができます。
下記が実装例です。

#!/usr/bin/perl

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を指定していない場合、プログラム全体の該当のシグナルハンドルを指定することになります。

上記のプログラムを実際にタイムアウトさせると下記のようになります。

[hiroki@capybara]~% ./sig_alarm.pl
What your name??:
ERROR: TIMEOUT
[hiroki@capybara]~%

今度はシェルからのメッセージではなく、きちんと自分自身のプログラムからのメッセージが表示されているのがわかると思います。


最後にalarmとsleepを併用することは推奨されません。
sleepがalarmを使って実装されていることが多く、正しく動かないことがあるからです。


alarmとevalとdieを組み合わせることでタイムアウトがないような処理にもタイムアウトが実装できるようになります。

About this Archive

This page is a archive of entries in the Perl category from July 2009.

Perl: September 2009 is the next archive.

Find recent content on the main index or look in the archives to find all content.