我正在尝试用 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 中捕获正在运行的后台进程的标准输出?