关于配合文件与单打文件的“xargs”错误

关于配合文件与单打文件的“xargs”错误

我目前正在使用 HISAT2,并尝试使用 xargs 让我在输入多个样本时变得更轻松。

所以我有一个文本文件“samples.txt”,其中每个样本名称用空格分隔 -

ERR199044 ERR188104 ERR188234 ERR188245 ...

我当前的命令行输入如下所示-

> cat ./samples.txt| xargs -I {} sh -c "./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 ./samples/{}_chrX_1.fastq.gz -2 ./samples/{}_chrX_2.fastq.gz -S ./map/{}_chrX.sam"

对于每个输入样本名称,我要使用的命令行输出的格式应如下所示:

./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 ./samples/ERR199044_chrX_1.fastq.gz -2 ./samples/ERR199044_chrX_2.fastq.gz -S ./map/ERR199044_chrX.sam

我收到一条错误消息,内容是 -

Warning: Same mate file "./chrX_data/samples/ERR199044" appears as argument to both -1 and -2
Extra parameter(s) specified: "ERR188104_chrX_1.fastq.gz", "ERR188104_chrX_2.fastq.gz", "ERR188104_chrX.sam"
Note that if <mates> files are specified using -1/-2, a <singles> file cannot
also be specified.  Please run bowtie separately for mates and singles.
Error: Encountered internal HISAT2 exception (#1)
Command: /mnt/c/Alex/Lab_Files/RNAseq/tools/hisat2-2.1.0/hisat2-align-s --wrapper basic-0 -p 8 --dta -x ./chrX_data/indexes/chrX_tran -S ./map/ERR199044 -1 ./chrX_data/samples/ERR199044 -2 ./chrX_data/samples/ERR199044 ERR188104_chrX_1.fastq.gz ERR188104_chrX_2.fastq.gz ERR188104_chrX.sam
(ERR): hisat2-align exited with value 1

因此,问题似乎是“{}”之后的所有内容都被忽略,使两个不同的文件看起来相同,并且 HISAT2 停止工作。

我不清楚的是“mates”和“singles”之间有什么区别,以及是否有任何方法可以解决这个问题,以便我可以输入相同的样本名称并让 Unix 理解它指定了具有该名称的多个不同文件在里面?

谢谢你!

答案1

xargs版本:

为此,您需要在空白处分割输入文件的每一行(这是xargs默认行为),然后使用 . 为每个单词xargs运行一次脚本。或者,您可以让 shell 脚本循环遍历“$@”。sh-n 1

您不能-I {}在此处使用,因为这会导致xargs一次读取输入文件一行。即使您将分隔符设置为空格-d ' ',当它开始读取下一个输入行时,您也会在每个输入行的末尾收到错误。

幸运的是,您根本不需要使用- 您已经只是将输入的回显字作为单独的参数-I {}附加到命令行末尾,这是“没有 -I 的默认行为”。shxargs

在 shell 脚本中,您可以通过位置参数(即 as )来引用参数$1,就像在任何 shell 脚本中一样。您可以$1在 shell 脚本中随意使用。

ps您还需要为命令提供参数 0(sh 进程的任意名称 - 字符串“sh”很方便 - 但如果您希望在 中轻松找到它,您可以使用任何名称) sh -c '...script...'。它必须是参数之后的第一个参数'...script...',并且将位于$0shell 脚本内。

所以,你需要做这样的事情(没有 for 循环):

xargs -n 1 sh -c \
  './hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran \
    -1 "./samples/$1_chrX_1.fastq.gz" -2 "./samples/$1_chrX_2.fastq.gz" \
    -S "./map/$1_chrX.sam"' sh < ./samples.txt

或(使用 for 循环):

xargs sh -c '
  for f in "$@"; do \
    ./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran \
      -1 "./samples/${f}_chrX_1.fastq.gz" -2 "./samples/${f}_chrX_2.fastq.gz" \
      -S "./map/${f}_chrX.sam"
  done' sh < ./samples.txt

for 循环版本会更快,因为它不需要执行sh多次(每个“单词”一次)。

它运行的sh次数尽可能少,限制是 shell 的最大命令行长度(在现代系统上约为 2MB)。除非samples.txt非常大(超过 200,000 个条目),否则意味着它只会运行sh一次。

shell while-read 循环:

像这样的工作不需要 xargs 。以下内容将在 bash 中运行,也可能-a在其他支持数组和read.

while read -a words; do
  for f in "${words[@]}"; do 
    ./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran \
      -1 "./samples/${f}_chrX_1.fastq.gz" \
      -2 "./samples/${f}_chrX_2.fastq.gz" \
      -S "./map/${f}_chrX.sam"
  done
done < samples.txt

这会将每个输入行的每个单词读入 bash 数组,然后(使用循环for遍历数组中的每个单词)使用适当的参数运行 hisat2 程序。

不过,请参阅:为什么使用 shell 循环处理文本被认为是不好的做法?

awk版本:

awk '{
  for (i=1;i<=NF;i++) {
    printf "./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 \"./samples/%s_chrX_1.fastq.gz\" -2 \"./samples/%s_chrX_2.fastq.gz\" -S \"./map/%s_chrX.sam\"\n", $i, $i,$i;
  }
}' ./samples.txt | sh

请注意,这会将 awk 的输出通过管道传递给 sh 来执行。如果没有该管道进入 sh,输出将如下所示:

./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 "./samples/ERR199044_chrX_1.fastq.gz" -2 "./samples/ERR199044_chrX_2.fastq.gz" -S "./map/ERR199044_chrX.sam"
./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 "./samples/ERR188104_chrX_1.fastq.gz" -2 "./samples/ERR188104_chrX_2.fastq.gz" -S "./map/ERR188104_chrX.sam"
./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 "./samples/ERR188234_chrX_1.fastq.gz" -2 "./samples/ERR188234_chrX_2.fastq.gz" -S "./map/ERR188234_chrX.sam"
./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran -1 "./samples/ERR188245_chrX_1.fastq.gz" -2 "./samples/ERR188245_chrX_2.fastq.gz" -S "./map/ERR188245_chrX.sam"

这也将运行得很快,因为 sh 脚本在 awk 脚本打印每一行时执行它。

或者,您可以sprintf将命令行放入变量中,而不是printf放入标准输出中。然后你可以使用awkssystem()函数直接执行它,类似于下面的 perl 示例:

珀尔版本:

perl -lane '
  foreach $f (@F) {
    system(qw(./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran),
      -1, "./samples/${f}_chrX_1.fastq.gz",
      -2, "./samples/${f}_chrX_2.fastq.gz",
      "-S", "./map/${f}_chrX.sam");
  };' ./samples.txt

它使用perlsystem()函数,因此它直接执行命令,而不需要通过管道传输到sh

对于测试运行,请在引号运算符echo后立即添加单词和空格。qw(

顺便说一句,在这个 perl 版本中添加代码来检查文件是否存在,和/或测试每次运行是否成功或对输出进行后处理比在 shell 或 awk 版本中更容易./hisat2-2.1.0/hisat2- 特别是如果它写成一个独立的剧本而不是一句台词。例如:

#!/usr/bin/perl -w

use strict;

while(<>) {
  foreach my $f (split) {
    my $f1 = "./samples/${f}_chrX_1.fastq.gz";
    my $f2 = "./samples/${f}_chrX_2.fastq.gz";
    my $sam = "./map/${f}_chrX.sam";

    if (!(-r $f1 && -r $f2 && -r $sam)) {
      warn "Missing or unreadable file for $f\n";
      next
    };

    my $rc = system(
        qw(echo ./hisat2-2.1.0/hisat2 -p 8 --dta -x ./indexes/chrX_tran),
        -1, $f1, -2, $f2, '-S', $sam
    );

    if ($rc) {
      warn "hisat2 returned non-zero exit code for $f: $rc\n";
    };
  }
}

相关内容