1

我正在尝试用 Perl 编写一个管理器来自动化我实验室一直在使用的生物信息学管道。(REPET 管道,任何感兴趣的人都可以使用。)管道有八个步骤,其中几个被分解为可以并行运行的子步骤。最值得注意的是,步骤 3 分为三个部分,步骤 4 分为三个相应的部分。step 3 的每个部分都可以独立运行,其对应的 step 4 部分可以在其 step 3 同伴完成后立即启动。我希望我的经理能够在三个并行线程中启动第 3 步,并且对于每个线程,在第 3 步完成后立即转到第 4 步。我能想到的最好方法是监控每个进程的输出。每个步骤的输出如下所示:

START TEdenovo.py (2012-08-23 11:20:10)
version 2.0
project name = dm3_chr2L
project directory = /home/<etc>
beginning of step 1
submitting job(s) with groupid 'dm3_chr2L_TEdenovo_prepareBatches' (2012-08-23 11:20:10)
waiting for 1 job(s) with groupid 'dm3_chr2L_TEdenovo_prepareBatches' (2012-08-23 11:20:10)
execution time per job: n=1 mean=2.995 var=0.000 sd=0.000 min=2.995 med=2.995 max=2.995
step 1 finished successfully
version 2.0
END TEdenovo.py (2012-08-23 11:20:25)

这是第 1 步的输出,但是在第 3 步中,当“第 3 步成功完成”出现在输出中时,可以安全地继续进行第 4 步。问题已经成功地将其中三个进程的输出制表,因为它们运行在一次。本质上,这是我想要的行为(伪代码):

my $log31 = `TEdenovo.py [options] &`;
my $log32 = `TEdenovo.py [options] &`;
my $log33 = `TEdenovo.py [options] &`;

while(1) {
    #start step 41 if $log31 =~ /step 3 finished successfully/;
    #start step 42 if $log32 =~ /step 3 finished successfully/;
    #start step 43 if $log33 =~ /step 3 finished successfully/;
    #monitor logs 41, 42, 43 similarly
    last if #all logs read "finished successfully"
    sleep(5);
}

#move on to step 5

问题是用反引号调用一个进程会导致 perl 等到该进程完成才能继续前进。正如我发现的那样,它与 system() 不同,您可以在其中使用 & 将某些内容旋转到后台进程中,然后立即继续。据我所知,没有一个好的方法可以使用 system() 来获得我正在寻找的效果。我想我可以这样做:

system("TEdenovo.py [options] & > log31.txt");

然后定期轮询 log31.txt 以查看是否出现“已成功完成”,但这似乎不必要的混乱。

我还尝试在文件句柄中打开该进程:

open(my $step3, "TEdenovo.py [options] |");
my @log3;

while(1)
{
    push(@log3, <$step3>);
    last if grep("step 3 finished successfully", @log3);
    sleep(5);
}

...但是,Perl 再一次等到进程完成才能继续前进(在这种情况下,在 push() 处)。我用 $| 尝试了上述方法 设置和取消设置。

所以,我的问题的本质是:有没有办法在 perl 中捕获正在运行的后台进程的标准输出?

4

2 回答 2

1

也许你可以试试

open(my $step3, "TEdenovo.py [options] |");

while(<$step3>)
{
    last if /step 3 finished successfully/;
}

而不是 while(1) ?

于 2012-08-23T19:23:24.277 回答
0

使用open和读取管道句柄方法是正确的方法。如果 Nahuel 在标量上下文中从句柄中读取的建议没有帮助,那么您可能仍然会受到缓冲的影响。

$|改变 Perl 输出的缓冲行为,但不会改变从 Perl 调用的任何外部程序的行为。您必须使用不缓冲其输出的外部程序。在这种情况下,我相信通过将-u选项传递给 python 是可能的:

open(my $step3, "|-", "python -u TEdenovo.py [more options]");
于 2012-08-23T19:28:19.900 回答