2

我是使用对象的新手,不明白为什么Try::Tiny不会catch出现我的try陈述中的错误。这是我正在尝试做的一个示例:

use strict;  
use warnings;
use Net::FTP;
use Try::Tiny;

my $TIMEOUT = 5;
my $HOST = 'replace_with_hostname';
my $USER = 'replace_with_username';
my $PASS = 'replace_with_password';
my @ERRORS;
my $ftp = undef;

sub my_sub {
  my $err = shift;
  push(@ERRORS,$err);
  goto END;
}

try {
  local $SIG{ALRM} = sub { die "new\n" };
  alarm $TIMEOUT;
  $ftp = Net::FTP->new($HOST, Passive=>1, DEBUG=>3, Timeout=>2);
  alarm 0;
}
catch {
  die $_ if $_ ne "new\n"; 
  my_sub("FTP: cannot connect to [$HOST]");
};

try {
  local $SIG{ALRM} = sub { die "login\n" };
  alarm $TIMEOUT;
  $ftp->login($USER,$PASS);
  alarm 0;
}
catch {
  die $_ if $_ ne "login\n"; 
  my_sub("FTP: cannot cannot log in");
};

END: {
  if (@ERRORS) {
    print "$ERRORS[0]\n";
    exit -1;
  }
  print "Success\n";
  exit 0;
}

我正在使用该goto功能,因为我最终将获得用于 WebDAV 和 CMIS 测试的块,并且希望在我点击之前捕获所有错误ENDtry-catch我可以通过像这样更改块来使前面做我想做的事情:

try {
  local $SIG{ALRM} = sub { die "new\n" };
  alarm $TIMEOUT;
  $ftp = Net::FTP->new($HOST, Passive=>1, DEBUG=>3, Timeout=>2) 
    or my_sub("FTP: cannot connect to [$HOST]");
  alarm 0;
}
catch {
  die $_ if $_ ne "new\n"; 
};

但是,在这两种情况下,我都觉得我没有很好地使用Try::Tiny

当然,我真正想做的是将try-catch块放入一个函数中,就像sid_com 所做的那样。这对我不起作用:

sub try_f {
  my $cmd = shift;
  my $msg = shift;
  try {
    local $SIG{ALRM} = sub { die "timeout '$cmd'\n" };
    alarm $TIMEOUT;
    \$cmd or my_sub($msg);
    alarm 0;
  }
  catch {
    die $_ if $_ ne "timeout '$cmd'\n"; 
  };
} # sub

try_f("$ftp = Net::FTP->new(Host=>$HOST, Passive=>1)", "FTP: cannot connect to [$HOST]");

try_f("$ftp->login($USER,$PASS)", "FTP: cannot cannot log in");

错误说$ftp是一个未初始化的值,但我不知道如何解决这个问题。

编辑:这是我收到的两个答案的工作综合。谢谢,Max 和 LeoNerd!

use strict;  
use warnings;
use Net::FTP;
use Try::Tiny;

my $HOST = 'replace_with_hostname';
my $USER = 'replace_with_username';
my $PASS = 'replace_with_password';
my @ERRORS;
my $ftp = undef;

sub try_f {
  my $timeout = shift;
  my $command = shift;
  my $message = shift;
  my $label   = shift;
  try {
    local $SIG{ALRM} = sub { die "timeout\n" };
    alarm $timeout;
    $command->() or die "module\n";
    alarm 0;
  }
  catch {
    if ( $_ eq "module\n" || $_ eq "timeout\n" ) {
      push(@ERRORS,"$message: $_");
      goto $label;
    }
    die $_;
  };
}

FTP_BLOCK: {
  try_f(4, sub { $ftp = Net::FTP->new($HOST, Passive=>1, Debug=>3) }, "FTP: cannot connect to [$HOST]", 'END');
  try_f(2, sub { $ftp->login($USER,$PASS) }, "FTP: cannot log in", 'END');
}

END: {
  if (@ERRORS) {
    foreach my $e (@ERRORS) { print "$e"; }
    exit -1;
  }
  print "Success\n";
  exit 0;
}
4

2 回答 2

4

Net::FTP->new不会die出错。相反,它只是返回undef. 因此,Try::Tiny无法确定出了什么问题,并且该catch块没有被执行。

而不是my_sub,使用

$ftp = Net::FTP->new($HOST, Passive=>1, DEBUG=>3, Timeout=>2) or die $@;

Try::Tiny 文档说block$@中不会出现错误,但表示如果返回,它将在此处放置错误消息。这就是为什么您可以中使用该消息的原因(如果需要,您也可以在这种情况下使用自己的消息)。catchNet::FTP->newundefdietry

同样,在 error 上login 返回false,因此您需要添加一个 " or die" 语句(不幸的是,login似乎不会在 error 上给出错误消息)。

于 2012-10-08T22:14:46.347 回答
2

您正在尝试在字符串中传递代码:

try_f("$ftp = Net::FTP->new(Host=>$HOST, Passive=>1)", "FTP: cannot connect to [$HOST]");

\$cmd or my_sub($msg);

那是行不通的。

我可以建议改为使用sub { }创建匿名代码引用吗?

try_f( sub { $ftp = ..... } );

然后稍后

$cmd->() or my_sub($msg);
于 2012-10-10T10:30:25.240 回答