プロが教えるわが家の防犯対策術!

いつもお世話になっております。

以下のようなスクリプトを書き、
imapサーバの死活監視を行おうと思っております。

#################################
my $imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or die "Cannot connect to $hostname imap:143. $!";

-------処理は続きますが割愛-------
#################################

そこで、1つ壁にぶつかったので質問いたしました。

+++壁の内容+++
imapサーバに何らかの理由で接続できなかったケースを考慮して、
die関数を使おうと思ったのですが、
このdie関数は、「引数に指定した値を標準エラー出力に出力」するとのことなのですが、
引数に指定した値を任意のファイルに出力する方法はないのでしょうか??

+++やりたいことの背景+++
imapサーバを監視するPerlスクリプトは、
実行するたびに指定したファイルに実行結果と実行時間を出力したい。

実行結果と実行時間を出力したファイルを
別の監視プログラムが監視するという二重構造の監視を行いたい。

imapサーバを監視するPerlスクリプトの実行結果を、
どうしても任意のファイルに出力したい。
+++++++++++++++++++++++++++++++++

以上です。

よろしくお願いします。

A 回答 (8件)

すいません、No.6の記述に間違いを発見しました。



$is_open = open my $fh, ">>", $logfile;
   ↓   ↓訂正
$is_open = open $fh, ">>", $logfile;
    • good
    • 0
この回答へのお礼

ryu_chan様

貴重なご意見をたくさんありがとうございます。

今回はevalを用いて要件を実現することにしましたが、
サブルーチンでdieを捕捉するという手法(考え方)、
非常に勉強になりました。
ありがとうございました!

これから検証して実際の動きをみながら作成したいと思います。

お礼日時:2009/12/11 10:52

> eval {


> my $imap = Mail::IMAPClient->new(
> Server => $hostname,
> User => $username,
> Password => $password,
> Timeout => 5,
> ) or die "1 \n";
> my $folders = $imap->folders or die "2 \n";
> $imap->logout();
>
> # );
> };

No2 です。「全体を」と書いたので、誤解を与えたかもしれません。「質問に書かれていたコード全体」という
意味です。上のコードの場合、or die "1 \n"; の次の行で eval を終了する必要があります。new() に失敗した場合は、
次の2行の $imap->folders と $imap->logout() を実行してはならないからです。また、new() に失敗した場合の
エラー処理は、直後に行う必要があります。

my $imap;
eval {
$imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or die "1 \n";
};

if ($@) {
open (FILE, ">>$logfile") or die "$!";
$data = localtime;
print FILE "$data FAILURE:$hostname port:143(imap4) failed. \n";
close (FILE);
} else {
my $folders = $imap->folders or die "2 \n";
$imap->logout();
...

もう1つですが、die "1 \n" という書き方は、何ら有益な情報を含んでいず、あまり適切とはいえません。
失敗の原因や失敗した行番号が必要なければ、eval や die を使わずに unless ($imap) { エラー処理 }
とすることもできます。
    • good
    • 0
この回答へのお礼

kumoz様

「スクリプトををご確認いただいて気になることがありましたら・・・」

などと図々しく確認をお願いしてしまったにもかかわらず、
適切な回答アドバイスをいただきありがとうございます!

ご教示いただいたevalの使い方でこれから検証し、
より精度の高い監視スクリプトを作成したいと思います。

お礼日時:2009/12/11 10:57

>その関数の中のdieを捕捉してエラーメッセージを$logfileに出力する、


>ということは可能なのでしょうか???

そのエラーメッセージを書き込むためのopen処理でまたエラーが出たら・・・
ってなると永遠に続きそうですね^^;

例えば以下のように、open処理を何回か繰り返して行い、ダメな場合はあきら
めるとかはいかかでしょうか?

my $try_count = 5;
my ($fh, $is_open);

for ( 1..$try_count ) {
$is_open = open my $fh, ">>", $logfile;
last if $is_open;
sleep 1;
}

if ( $is_open ) {
print {$fh} "$date:@_";
close $fh;
}

もっといい方法を誰かが提示してくれるかもしれません。
    • good
    • 0

シグナルの無効化(デフォルト状態に戻す)は、以下でもいいようです。


(互換性からこちらのほうがいいのかしら)

$SIG{__DIE__} = 'DEFAULT';

ところで、以下のようにエラー処理をする関数を作るのではダメなのでしょうか?

sub error {
open my $fh, ">", "error.log";
print {$fh} @_;
close $fh;

die @_;
}

my $imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or error "Cannot connect to $hostname imap:143. $!";
    • good
    • 0
この回答へのお礼

ryu_chan様

早速の追加回答ありがとうございます!
教えていただいた方法をもとに検証してみたところ、
以下のスクリプトでdieの標準エラー出力を任意のファイルに出力できました!

####################################
my $logfile = "/var/log/imap4.log";

sub error {
my $date = localtime;
open my $fh, ">>", "$logfile";
print {$fh} "$date:@_";
close $fh;

die @_;
}
my $imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or error "Cannot connect to $hostname imap:143.$!\r\n";
$imap->logout();

exit
#####################################

ちなみに、errorという関数の中で、
$logfileがopenできなかったことを考慮してor die処理を入れたとした場合、
その関数の中のdieを捕捉してエラーメッセージを$logfileに出力する、ということは可能なのでしょうか???

お礼日時:2009/12/10 11:59

その通りです。


dieを実行する前にシグナルを定義しておきます。

スコープを作って、dieを捕捉する範囲を限定するといいかもしれません。

{
local $SIG{__DIE__} = sub {
open my $fh, ">", "data.txt";
print {$fh} @_;
close $fh;
};

die "just die";
}

あるいは、捕捉すべきdieがなくなった段階でdeleteするとかでしょうか。

delete $SIG{__DIE__};
    • good
    • 0

__DIE__擬似シグナルを用いて、dieをトラップするのはどうでしょうか?



$SIG{__DIE__} = sub {
open my $fh, ">", "error.log";
print {$fh} @_;
close $fh;
};
    • good
    • 0
この回答へのお礼

ryu_chan様

回答ありがとうございます。

教えて頂いた方法について
私の認識に間違いがないか確認させてください。

◆私の認識
dieのシグナルに反応しさらにその実行結果をファイルに保存する、
という関数を用意し、スクリプト内のdieが実行される前の部分に配置しておく

という理解で間違いないでしょうか。

よろしくお願します。

お礼日時:2009/12/10 09:17

全体を eval ブロックで囲むと、die のエラーもトラップされて $@ にセットされると思います。


次は別の例ですが、(1) と (2) は同じエラーメッセージが表示されます。

(1)
open FH, "not_exist" or die "Can't open not_exist: $!";

(2)
eval {
open FH, "not_exist" or die "Can't open not_exist: $!";
};

if ($@) {
print $@;
exit;
}
    • good
    • 0
この回答へのお礼

kumoz様

ご回答ありがとうございます。

全体を eval ブロックで囲むという方法は、
ここに質問したあと私なりに色々調べて行き当たりました。

そして、以下のようなサンプルスクリプトを作成しましたところ、
期待とおりの結果がえられました。

##################################################
$logfile = "/var/log/imap4.log";

#my $imap;
#my $folders;
eval {
my $imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or die "1 \n";
my $folders = $imap->folders or die "2 \n";
$imap->logout();

# );
};
###### Error judgment #####
if ($@) {
open (FILE, ">>$logfile") or die "$!";
$data = localtime;
print FILE "$data FAILURE:$hostname port:143(imap4) failed. \n";
close (FILE);
} else {
open (FILE, ">>$logfile") or die "$!";
$data = localtime;
print FILE "$data SUCCESS:$hostname port:143(imap4) logged in. \n";
close (FILE);
}
exit
##################################################

スクリプトをご確認いただいて気になることがありましたら、
ご指摘いただけますと幸いです。

よろしくお願します。

お礼日時:2009/12/10 09:26

やったことはないけど


open(STDERR, ">/some/path/to/file");
みたいなことはできないかなぁ. 必要ならバッファリングも何とかする.
    • good
    • 0
この回答へのお礼

Tacosan様

ご回答ありがごとうございます。

open(STDERR, ">/some/path/to/file");

という方法で私のやりたいことを試すとした場合、
細かい話で恐縮ですが、
以下のようなスクリプトですと、

my $imap = Mail::IMAPClient->new(
Server => $hostname,
User => $username,
Password => $password,
Timeout => 5,
) or die "Cannot connect to $hostname imap:143. $!";

どの部分に挿入したらよいのでしょうか。

Perl初学者のため、基本的なことに対する理解が浅いため、
質問させていただきました。

お手数おかけしますが、ご教示いただきますと幸いです。

よろしくお願します。

お礼日時:2009/12/10 09:29

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!