我的问题类似于: Is it possible for a Perl subroutine to force its caller to return? 但我需要程序方法。
我想用return来编写一些消息过程,示例基本代码:
sub PrintMessage {
#this function can print to the screen and both to logfile
print "Script message: $_[0]\n";
}
sub ReturnMessage {
PrintMessage($_[0]);
return $_[2]; # <-- we thinking about *this* return
}
sub WorkingProc {
PrintMessage("Job is started now");
#some code
PrintMessage("processed 5 items");
# this should return from WorkingProc with given exitcode
ReturnMessage("too many items!",5) if $items>100;
#another code
ReturnMessage("time exceded!",6) if $timespent>3600;
PrintMessage("All processed succesfully");
return 0;
}
my $ExitCode=WorkingProc();
#finish something
exit $ExitCode
想法是,如何使用ReturnMessage函数内部的返回来使用WorkingProc函数中的指定代码退出?注意,从许多地方调用ReturnMessage函数。
答案 0 :(得分:3)
这是不可能的。但是,您可以明确返回:
sub WorkingProc {
PrintMessage("Job is started now");
...
PrintMessage("processed 5 items");
# this returns from WorkingProc with given exitcode
return ReturnMessage("to much items!", 5) if $items > 100;
...
return ReturnMessage("time exceded!", 6) if $timespent > 3600;
PrintMessage("All processed succesfully");
return 0;
}
一个sub可以有任意数量的return语句,所以这不是问题。
这种解决方案优于黑客通过调用堆栈,因为控制流程对读者来说更为明显。你梦寐以求的是一种GOTO
,大多数人不写C或BASIC等已经放弃了45年前。
您的代码依赖退出代码来确定子例程中的错误。 *叹*。 Perl有一个相当倒退的异常系统,但仍然比 更先进。
对die "Reason"
或use Carp
和croak "Reason"
犯了致命错误。使用Try::Tiny
或TryCatch
模块捕获错误。
sub WorkingProc {
PrintMessage("Job is started now");
...
PrintMessage("processed 5 items");
# this should return from WorkingProc with given exitcode
die "Too much items!" if $items > 100;
...
die "Time exceeded" if $timespent > 3600;
PrintMessage("All processed succesfully");
return 0;
}
WorkingProc();
如果抛出错误,则会以非零状态退出。
答案 1 :(得分:2)
为非本地返回而想到的方法是从最里面的函数抛出异常(die)。
然后你需要有一些包装代码来处理它在顶层。您可以设计一组实用程序例程来自动设置它。
答案 2 :(得分:2)
将Log::Any和Log::Any::Adapter与Exception::Class结合使用,您可以将所有部分放在一起,而且操作简单,灵活性最高:
#!/usr/bin/env perl
package My::Worker;
use strict; use warnings;
use Const::Fast;
use Log::Any qw($log);
use Exception::Class (
JobException => { fields => [qw( exit_code )] },
TooManyItemsException => {
isa => 'JobException',
description => 'The worker was given too many items to process',
},
TimeExceededException => {
isa => 'JobException',
description => 'The worker spent too much time processing items',
},
);
sub work {
my $jobid = shift;
my $items = shift;
const my $ITEM_LIMIT => 100;
const my $TIME_LIMIT => 10;
$log->infof('Job %s started', $jobid);
shift @$items for 1 .. 5;
$log->info('Processed 5 items');
if (0.25 > rand) {
# throw this one with 25% probability
if (@$items > $ITEM_LIMIT) {
TooManyItemsException->throw(
error => sprintf(
'%d items remain. Limit is %d.',
scalar @$items, $ITEM_LIMIT,
),
exit_code => 5,
);
}
}
{ # simulate some work that might take more than 10 seconds
local $| = 1;
for (1 .. 40) {
sleep 1 if 0.3 > rand;
print '.';
}
print "\n";
}
my $time_spent = time - $^T;
($time_spent > $TIME_LIMIT) and
TimeExceededException->throw(
error => sprintf (
'Spent %d seconds. Limit is %d.',
$time_spent, $TIME_LIMIT,
),
exit_code => 6);
$log->info('All processed succesfully');
return;
}
package main;
use strict; use warnings;
use Log::Any qw( $log );
use Log::Any::Adapter ('Stderr');
eval { My::Worker::work(exceptional_job => [1 .. 200]) };
if (my $x = JobException->caught) {
$log->error($x->description);
$log->error($x->error);
exit $x->exit_code;
}
示例输出:
Job exceptional_job started Processed 5 items ........................................ The worker spent too much time processing items Spent 12 seconds. Limit is 10.
或
Job exceptional_job started Processed 5 items The worker was given too many items to process 195 items remain. Limit is 100.