파일과 싱글 파일의 짝짓기 관련 "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가 작동을 멈춘다는 것입니다.

나에게 명확하지 않은 것은 "메이트"와 "싱글"의 차이점이 무엇인지, 동일한 샘플 이름을 입력하고 Unix가 해당 이름을 가진 여러 개의 다른 샘플을 지정한다는 것을 이해할 수 있도록 이 문제를 해결할 수 있는 방법이 있는지 여부입니다. 거기에?

감사합니다!

답변1

xargs 버전:

이렇게 하려면 입력 파일의 각 행을 공백으로 분할한 다음(이것이 xargs기본 동작임) 를 xargs사용하여 각 단어에 대해 스크립트를 한 번씩 실행해야 합니다. 또는 "$@"을 통해 쉘 스크립트 루프를 가질 수 있습니다.sh-n 1

입력 파일을 한 번에 한 줄씩 읽게 되므로 -I {}여기서는 사용할 수 없습니다 . xargs구분 기호를 공백으로 설정하더라도 -d ' '다음 입력 줄을 읽기 시작할 때 각 입력 줄의 끝 부분에서 오류가 발생합니다.

다행스럽게도 이를 전혀 사용할 필요가 없습니다. 이미 -I {}입력의 에코 단어를 명령줄 끝에 별도의 인수로 추가하고 있는데, 이는 "-I 없는 기본 동작"입니다.shxargs

$1쉘 스크립트에서는 다른 쉘 스크립트와 마찬가지로 위치 매개변수(즉, )를 통해 매개변수를 참조합니다 . $1쉘 스크립트에서 자유롭게 사용할 수 있습니다 .

ps또한 명령에 인수 0을 제공해야 합니다(sh 프로세스에 대한 임의의 이름 - "sh" 문자열이 편리합니다. 그러나 sh -c '...script...'. 이는 매개변수 다음의 첫 번째 매개변수여야 하며 쉘 스크립트 내에 '...script...'있습니다 .$0

따라서 다음과 같이 수행해야 합니다(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

shfor 루프 버전은 여러 번(각 "단어"에 대해 한 번) 실행할 필요가 없으므로 더 빠릅니다 .

sh쉘의 최대 명령줄 길이(최신 시스템에서는 약 2MB)에 의해 제한되어 가능한 한 적은 횟수로 실행됩니다 . 매우 큰 경우(200,000개 이상의 항목) 가 아니면 한 번만 samples.txt실행된다는 의미입니다 sh.

쉘 읽는 동안 루프:

이와 같은 작업에는 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 프로그램을 실행합니다.

그러나 다음을 참조하십시오.쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?

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대신 명령줄을 변수에 넣을 수도 있습니다. 그런 다음 다음 Perl 예제와 유사하게 s 함수를 printf사용하여 직접 실행할 수 있습니다 .awksystem()

진주 버전:

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

perl함수 를 사용 system()하므로 명령을 sh.

테스트 실행의 경우 인용 연산자 echo바로 뒤에 단어와 공백을 추가합니다 .qw(

그런데, 이 Perl 버전에 코드를 추가하여 파일이 존재하는지 확인하고/하거나 각 실행의 성공 여부를 테스트하거나 셸 또는 awk 버전보다 출력을 후처리하는 것이 더 쉽습니다. ./hisat2-2.1.0/hisat2특히 독립형 A 스크립트로 작성된 경우 더욱 그렇습니다. 선보다는. 예를 들어:

#!/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";
    };
  }
}

관련 정보