3

我正在尝试修改 perl 脚本。这是我要修改的部分:

原来的:

        system ("tblastn -db $BLASTDB -query $TMP/prot$$.fa \\
             -word_size 6 -max_target_seqs 5 -seg yes -num_threads $THREADS -lcase_masking \\
             -outfmt \"7 sseqid sstart send sframe bitscore qseqid\"\\
             > $TMP/blast$$") && die "Can't run tblastn\n";

我正在尝试用以下内容替换系统(“tblastn .....”):

system ("cat $TMP/prot$$.fa | parallel --block 50k --recstart '>' --pipe \\ tblastn -db $BLASTDB -query - -word_size 6 -outfmt \'7 sseqid sstart send sframe bitscore qseqid\' -max_target_seqs 5 -seg yes -lcase_masking > $TMP/blast$$") && die "Can't run tblastn\n";

这用 GNU parallel 替换了普通的 tblastx 程序,它通过管道传输 tblastx 命令。在 bash 中运行上述命令(用实际文件替换临时输入)可以完美运行,但是当 perl 脚本尝试执行它时,错误日志(对于 tblastx)说它在 sseqids 之后过早终止。如果您在 bash 中运行没有转义字符的相同命令,则会发生相同的错误。

因此,我假设错误是由于“7 ssequids sstart ...”周围的单引号没有被正确解析。我不确定如何在 perl 中正确地做嵌套引号。我认为我做对了,因为它通过 bash 而不是通过 perl 脚本工作。我查看了很多 perl 文档,所有内容都说转义字符 \ 应该与引号或双引号一起使用。但是对于我的例子来说它不起作用。

有人可以提供关于为什么没有处理报价的输入吗?

4

2 回答 2

1

这里的问题几乎可以肯定是引用插值。每次您“掏出”时,您都会打开另一层引号。它在引号内的作用是一个问题,即您是在使用双引号"- 它是插值还是单引号',然后在传递到下一个shell 之前将其视为文字。

查看perlopperl 如何引用。我建议您尝试组装这样的命令:

my $parallel = q{parallel --block 50k --recstart '>' --pipe};
my $outfmt = q{'7 sseqid sstart send sframe bitscore qseqid'};

print $parallel,"\n";
print $outfmt,"\n";

my $command = "cat $TMP/prot$$.fa | $parallel \\ tblastn -db $BLASTDB -query - -word_size 6 -outfmt $outfmt -max_target_seqs 5 -seg yes -lcase_masking > $TMP/blast$$";

print $command; 
system ( $command );

(显然在将它传递给系统之前检查您的“命令”是否正确)

但是我可以建议一种不同的方法吗?怎么样而不是嵌入catparallel你可以在perl.

我很害怕我对你正在运行的命令并不完全熟悉,但它会是这样的:

#!/usr/bin/perl

use strict;
use warnings;

open( my $input, "<", "$TMP/prot$$.fa" ) or die $!;

my $fork_manager = Parallel::ForkManager->new($THREADS);

while ( my $line = <$input> ) {
    $fork_manager->start and next;
    chomp $line;
    system(
        "tblastn -db $BLASTDB -query $line \\
                 -word_size 6 -max_target_seqs 5 -seg yes  -lcase_masking \\
                 -outfmt \"7 sseqid sstart send sframe bitscore qseqid\"\\
                 > $TMP/blast$$"
    ) && die "Can't run tblastn\n";
    $fork_manager->finish;
}
close ( $input );

如果需要输出合并,我可能会改用线程:

#!/usr/bin/perl

use strict;
use warnings;
use IPC::Open2;
use threads;
use Thread::Queue; 

my $num_threads = 8; 

my $work_q = Thread::Queue -> new(); 
my $results_q = Thread::Queue -> new(); 

sub worker {
    open2 ( my $blast_out, my $blast_in, "tblastn -db $BLASTDB -query - -word_size 6 -outfmt '7 sseqid sstart send sframe bitscore qseqid' -max_target_seqs 5 -seg yes -lcase_masking");
    while ( my $query = $work_q -> dequeue ) {
        print {$blast_in} $query;
        $results_q -> enqueue ( <$blast_out> ); #one line - you'll need something different for multi-line results.
    }
    close ( $blast_out );
    close ( $blast_in ); 
}

sub collate_results {
    open ( my $output, "$TMP/results.$$" ) or die $!; 
    while ( my $result = $results_q -> dequeue ) {
        print {$output} $result,"\n"; 
    }
    close ( $output ); 
}

my @workers; 
for (1..$num_threads) {
    push ( @workers, threads -> create ( \&worker ) ); 
}

my $collator = threads -> create ( \&collate_results ); 

open( my $input, "<", "$TMP/prot$$.fa" ) or die $!;
while ( my $line = <$input> ) {
    chomp $line;
    $work_q -> enqueue ( $line ); 
}
close ( $input );
$work_q -> end;

foreach my $thr ( @workers ) { 
    $thr -> join(); 
}
$results_q -> end;

$collator -> join; 

现在我很欣赏这两个可能看起来有点复杂和复杂。但是它们更多地是关于如何将 perl 扩展到并行执行的示例,因为这样做与运行 perl 相比,您拥有更多的范围和灵活性,但要“全力以赴”去做事情。

于 2015-05-06T08:49:22.857 回答
1

引用是个婊子。引用两次是个婊子²。

首先检查您认为正在运行的内容是否实际上正在运行。在这里print STDERR可以创造奇迹。

在你的情况下,我认为这将解决它:

my $TMP = $ENV{'TMP'};
my $BLASTDB = $ENV{'BLASTDB'};
my $cmd = qq{cat $TMP/prot$$.fa | parallel --block 50k --recstart '>' --pipe tblastn -db $BLASTDB -query - -word_size 6 -outfmt \\''7 sseqid sstart send sframe bitscore qseqid'\\' -max_target_seqs 5 -seg yes -lcase_masking > $TMP/blast$$};
print STDERR $cmd,"\n"; # Remove this when it works.
system($cmd) && die "Can't run tblastn\n";

如果您要重新读$TMP/blast$$入并删除它,您可以这样做:

my $TMP = $ENV{'TMP'};
my $BLASTDB = $ENV{'BLASTDB'};
open(my $fh, "-|", qq{cat $TMP/prot$$.fa | parallel --block 50k --recstart '>' --pipe tblastn -db $BLASTDB -query - -word_size 6 -outfmt \\''7 sseqid sstart send sframe bitscore qseqid'\\' -max_target_seqs 5 -seg yes -lcase_masking}) || die "Can't run tblastn\n";
while(<$fh>) { ... }
close $fh;

这将避免创建临时文件,并且如果$TMP可以被攻击者写入,这也会填补安全漏洞。作为额外的奖励,您将更早地获取数据,因为您不必等到每项工作都完成。

于 2015-05-06T10:13:37.640 回答