2

即使脚本在以后多次执行,如何使部分代码在 perl “第一次执行时只运行一次”

4

4 回答 4

2

您可以将那部分代码放在某个if块中进行测试,例如使用文件,无论它是否是脚本的第一次执行,如果是,则仅执行该块。

于 2012-12-25T21:37:44.570 回答
2

一种可能的方法是重写脚本本身,如下所示:

use warnings;
use script;

#### RUN ONCE BEGIN

do_something_once_only();

my $script_name = $0;
my $script_copy = $0 . 'old';
open my $fin, '<', $script_name or die $!;
open my $fout, '>', $script_copy or die $!;

my $script_content = do { local $/ = undef; <$fin>; };
$script_content =~ s/#### RUN ONCE BEGIN.+?#### RUN ONCE END/s;
print $fout $script_content;
`cp $script_copy $script_name`;

#### RUN ONCE END

do_something_else();

这只是一个概念;不幸的是,不能在这里测试它。但基本思想非常简单:在脚本中创建一个明确分隔的“运行一次”块,然后在第一次执行时,从脚本内容中删除该块。

于 2012-12-25T21:45:21.980 回答
1

我会开发一个类对象来用非常清楚的英语来完成它的工作。在 Ubuntu 中,在 /var/run/[run_name]/ 下为您的脚本创建一个新目录,并在第一次运行时使用该脚本在其中创建一个文件。例如:

文件名:RunOnce.pm

package RunOnce;
use Moose;

has 'run_name' => ( is => 'ro' , isa => 'Str' , required => 1 );
has 'initialize_file' => ( is => 'ro' , isa => 'Str'
    , builder => '_build_initialize_file'
    , lazy => 1 );

sub _build_initialize_file {
    return '/var/run/'. $_[0]->run_name . '/run_once.txt';
}

sub is_initialized {
    my $self = shift;
    return ( -f $self->initialize_file );
}

sub initialize {
    my $self = shift;
    open(my $init_file,'>' . $self->initialize_file )
         || die "Could not open initialize file "
            . $self->initialize_file . "! $!";
    $self->process_initial_run();
    print $init_file "Initialized on " . localtime(time) . "\n";
    close($init_file);
}

sub process_initial_run {
    warn "I don't do anything yet.";
}

package main;

my $runner = RunOnce->new(run_name => 'test_run');
$runner->initialize unless $runner->is_initialized();

1;

我刚刚在我自己的盒子上测试了这个:

paul@paul-1204-virtualbox:~$ sudo rm -rf /var/run/test_run/
paul@paul-1204-virtualbox:~$ sudo mkdir /var/run/test_run/
paul@paul-1204-virtualbox:~$ sudo chown paul:paul /var/run/test_run
paul@paul-1204-virtualbox:~$ perl RunOnce.pm
I don't do anything yet. at RunOnce.pm line 29.
paul@paul-1204-virtualbox:~$ perl RunOnce.pm
paul@paul-1204-virtualbox:~$

现在您可以覆盖包和类并执行您想要的操作,并且能够多次使用它来处理不同的项目,而无需太多工作:

新文件 RunOnceOverride.pm:

package RunOnceOverride;

use Moose;

extends 'RunOnce';

sub process_initial_run {
    warn "I'm going to try to do something else!";
}

package main;

my $runner = RunOnceOverride->new(run_name => 'test_run_override');
$runner->initialize unless $runner->is_initialized();

1;

现在:

paul@paul-1204-virtualbox:~$ sudo rm -rf /var/run/test_run_override/
paul@paul-1204-virtualbox:~$ sudo mkdir /var/run/test_run_override/
paul@paul-1204-virtualbox:~$ sudo chown paul:paul /var/run/test_run_override/
paul@paul-1204-virtualbox:~$ perl -I. RunOnceOverride.pm
I'm going to try to do something else! at RunOnceOverride.pm line 8.
paul@paul-1204-virtualbox:~$ perl -I. RunOnceOverride.pm
paul@paul-1204-virtualbox:~$ 

这是该问题的一个很好的可扩展解决方案,但 initialize 可能应该检查 is_initialized 本身,而不是期望主代码来执行它。

于 2012-12-25T22:18:58.860 回答
1

你不能做这样的事情:

mySub if (!(-e "/some/path/cpuid.txt"));

sub mySub() {
    //do something
    open(my $cpuid, ">", "/some/path/cpuid.txt") || die $!;
    print $cpuid "blah";
}

顺便说一句,我不是任何形式的 DRM 的忠实粉丝。另外,您建议这样做的方式将很容易被打破。只需删除 cpuid.txt 文件即可。

于 2012-12-26T08:41:57.807 回答