我用一个小模块包装了Perl的Net :: SSH :: Expect,以减少编写用于HP iLO卡的新配置脚本所需的样板代码.虽然我一方面希望这个包装器尽可能精简,但非程序员同事可以使用它,我也希望它尽可能好地编写.
它的使用方式如下:
my $ilo = iLO->new(host => $host, password => $password); $ilo->login; $ilo->command("cd /system1"); $ilo->command("set oemhp_server_name=$system_name", 'status=0');
这是iLO::command()
:
sub command { my ($self, $cmd, $response) = @_; $response = 'hpiLO-> ' unless defined($response); # $self->{ssh} is a Net::SSH::Expect object croak "Not logged in!\n" unless ($self->{ssh}); $self->{ssh}->send($cmd); if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { return { before => $self->{ssh}->before(), match => $self->{ssh}->match(), after => $self->{ssh}->after(), }; } else { carp "ERROR: '$cmd' response did not match /$response/:\n\n", $self->{ssh}->before()), "\n"; return undef; } }
我有两个相关的问题.首先,我应该如何处理与预期响应不匹配的响应?我想我现在正在做的事情是令人满意的 - 通过返回undef
我发出信号,我croak()
会输出一个错误(虽然不是很优雅).但感觉就像代码味道.如果Perl有异常,我会提出一个并让调用代码决定是否忽略它/退出/打印一个警告,但它没有(好吧,在5.8中).也许我应该返回一些其他iLO::response
带有错误信息的对象(或其他东西)和$ilo->before()
(只是Net :: SSH :: Expect's before()
)的内容?但是,如果我这样做 - 并且必须将每个人都包裹$ilo->command
在测试中以便捕获它 - 我的脚本将再次充满样板.
其次,我应该回归什么才能获得成功?同样,我的哈希包含来自Net :: SSH :: Expect的响应或多或少可以完成工作,但它不会以某种方式感觉"正确".虽然这个例子在Perl我的代码在其他语言中发出了同样熟悉的气味:我从来不知道如何或从一个方法返回什么.你能告诉我什么?
在Perl中引发异常的常用方法是使用die.通常的办法赶上他们使用的eval与块作为参数,和EVAL完成后测试$ @.
如果你熟悉的,如Java语言异常,然后想die
作为throw
和eval
作为try
和catch
.而不是返回undef
,你可以做这样的事情:
if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { return { before => $self->{ssh}->before(), match => $self->{ssh}->match(), after => $self->{ssh}->after(), }; } die "ERROR: '$cmd' response did not match /$response/:\n\n" . $self->{ssh}->before();
然后,在您的调用代码中:
eval { $ilo->command("set oemhp_server_name=$system_name", 'status=0'); }; if ( my $error = $@ ) { # handle $error here }
就像其他语言中的异常一样,这允许您在任何时候摆脱子方法,而不必担心在调用堆栈中传播返回值.它们将被eval
发现它们的第一个块捕获.此外,您可以die
再次重新抛出一个无法处理备份的异常.
更好的是,您可以使用die
抛出一个对象,您的异常处理程序可以查询该对象以获取有用的信息和错误消息.我喜欢为此目的使用Exception :: Class.该错误模块提供了这样做类似Java的try/catch块,还有一些语法糖.