哇哦;当两个文件共享一个公共 header 时从两个文件中获取多行

awk; getting multiple lines from two files when they share a common header

我有一个问题与有关此主题的许多其他问题非常相似,但我无法将这些解决方案扩展到我正在寻找的确切输出。

我有两个以 fastq 格式格式化的文件,看起来像这样:

file1.txt

@header:with:id:number:0001 1:this:number:indicates:pair:number
ABCD
+
1324
@header:with:id:number:0001 2:this:number:indicates:pair:number
EFGH
+
5678
@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0003 1:this:number:indicates:pair:number
IJKL
+
3456
@header:with:id:number:0003 2:this:number:indicates:pair:number
MNOP
+
7890

file2.txt

@header:with:id:number:0004 1:this:number:indicates:pair:number
QRST
+
1324
@header:with:id:number:0004 2:this:number:indicates:pair:number
UVWX
+
5678
@header:with:id:number:0005 1:this:number:indicates:pair:number
CDEF
+
3456
@header:with:id:number:0005 2:this:number:indicates:pair:number
GHIJ
+
7890
@header:with:id:number:0002 1:this:number:indicates:pair:number
YZAB
+
9012

每个 'block' 有四行,其中第一行(header)总是以 @ 开头并包含一个 id-number(例如 0001)和一个索引(即 1 或 2在 'space') 之后。 每个 id-number 都应在具有两个索引的同一文件中出现两次(如上例中除 0002 之外的所有 id-number 都是如此)。 现在我想把id-number出现在两个文件中的块分开存储(表示在任一文件中只出现一次的块)。

在这种情况下,输出应该是:

@header:with:id:number:0002 1:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0002 2:this:number:indicates:pair:number
YZAB
+
9012

这些行应该从原始文件中删除。

为此,我到目前为止使用 awk 和以下命令

awk -F" " '/^@/ && NR==FNR {lines[]; next}
     in lines {x=NR+3}
    (NR<=x) {print [=14=]}' file2.txt file1.txt

这输出:

@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012

到一半了。

我的问题是,如何在两个文件中出现的header中搜索id-number,并将它们存储在第三个文件中并从两个原始文件中删除相应的块?

您可以使用此 gnu awk 打印每个文件只出现一次的所有 headers:

awk -v ORS= -v RS='@header:' -F '[:[:blank:]]+' 'NF {
   if ( in seen)
      delete seen[]
   else
      seen[] = prt [=10=]
}
ENDFILE {
   for (i in seen)
      print seen[i]
   delete seen
}
{prt = RT}' file1 file2


@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0002 1:this:number:indicates:pair:number
YZAB
+
9012

使用 GNU awk:

awk 'BEGIN { 
             RS="@header" # Set the input record separator
           } 
   FNR==NR { # process the first file
             ORS="@header"; # Set the output record separator
             split([=10=],map,":"); # Split the record into array map using ":" as the delimiter
             map1[substr(map[5],1,4)]=[=10=] # map[5] will be e.g 0002 2. We only want 0002 and so use substr to create an index for array map1 with the record as the value
           } 
   NR!=FNR { # process the second file
             ORS="@header";
             split([=10=],map,":");
             id=substr(map[5],1,4); # id e.g. 0002
             if (id in map1) { 
                               print [=10=]; # If id in map1 array print this record
                               print map1[id] # if id in map1 array print array value
             } 
            }' file1.txt file2.txt

一个班轮:

awk 'BEGIN { RS="@header" } FNR==NR { ORS="@header";split([=11=],map,":");map1[substr(map[5],1,4)]=[=11=] } NR!=FNR { ORS="@header";split([=11=],map,":");id=substr(map[5],1,4);if (id in map1) { print [=11=];print map1[id] } }' file1.txt file2.txt

您能否尝试按照显示的示例进行编写和测试,我相信应该可以在任何 awk 中工作,但只能在 GNU awk 中进行测试。

awk '
FNR==NR{
  if([=10=]~/^@/){
    match([=10=],/^@header:with:id:number:[0-9]{4}/)
    mat1=substr([=10=],RSTART,RLENGTH)
    arr1[mat1]++
  }
    val1[mat1]=(val1[mat1]?val1[mat1] ORS:"")[=10=]
    next
}
{
  if([=10=]~/^@/){
    match([=10=],/^@header:with:id:number:[0-9]{4}/)
    mat2=substr([=10=],RSTART,RLENGTH)
    arr2[mat2]++
  }
  val2[mat2]=(val2[mat2]?val2[mat2] ORS:"")[=10=]
}
END{
  for(key1 in arr1){
    if(arr1[key1]==1 && arr2[key1]==1){print val1[key1] ORS val2[key1] }
  }
}' file1.txt file2.txt 

这将在两个文件中查找匹配索引的计数为 1,如果您希望在任何一个文件中有 1 计数,然后将 arr1[key1]==1 && arr2[key1]==1 更改为 arr1[key2]==1 在上述条件下。

输出结果如下所示。

@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0002 1:this:number:indicates:pair:number
YZAB
+
9012