我用一个小模块包装了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的before()
)?但是,如果我这样做 - 并且必须将每个$ilo->command
包装在一个测试中以便捕获它 - 我的脚本将再次充满样板。
其次,我应该回归什么才能获得成功?同样,我的哈希包含来自Net :: SSH :: Expect的响应或多或少可以完成工作,但它不会以某种方式感觉“正确”。虽然这个例子在Perl我的代码在其他语言中发出了同样熟悉的气味:我从来不知道如何或从一个方法返回什么。你能告诉我什么?
答案 0 :(得分:5)
答案 1 :(得分:5)
如果您熟悉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用于此目的。 Error模块也为类似Java的try / catch块提供了一些语法糖。
答案 2 :(得分:4)
你会在googlespace中找到很多关于这类事情的讨论。无论你决定什么,最佳做法是不重载任何值,因此返回值意味着不同的东西。它应该始终是一个错误代码,或者它永远不应该是错误代码。人们不应该查看实际值来判断它是否是错误代码。
查看CPAN(或您已经使用过的)上流行的Perl模块,了解它们的功能。我甚至在Mastering Perl中谈了这个,但我没有给出一个黑白回答。与所有真实代码一样,真正的答案是“它取决于”。
有很多不同的方法可以做到这一点。不幸的是,这意味着人们在各方面都这样做。既然如此,我调用一致性作为最重要的规则。大多数代码已经做了什么(不计算错误的方式)?如果我必须适应现有的代码库,我会尝试使用与大多数代码已经使用的相同类型的接口。
如果没有明确的赢家,请使用几种不同的样式编写用例。哪个适合问题更好或更自然地表达大多数用户将采取的步骤?这不仅仅是die
和eval
的阅读。使用未实现的接口编写示例脚本。你想要使用哪种风格?我发现在实现界面之前实际编写脚本比我想的要多得多。如果我正在为其他人写东西,我会向他们展示不同风格的剧本,并问他们哪个更好。
并且,如果所有这些都失败了,那就达到2d6。 :)
答案 3 :(得分:0)
除了使用“die”作为例外之外,您还可以添加另一种方法:
if (!$ilo->commandSucceeded("set oemhp_server_name=$system_name", 'status=0')) {
#recover here
}
当然,command()的内部实现变为
die ... if !commandSucceeded;