【问题标题】:awk to match string from file against another file and get previous and next 2 linesawk 将文件中的字符串与另一个文件匹配并获取前两行和下两行
【发布时间】:2021-03-18 05:27:38
【问题描述】:

我正在尝试将一个文件中的字符串与另一个文件进行匹配,以获取匹配的行以及前两行和下两行。

我可以用 grep 来处理一个chuck文件,但是会在原始文件(200M 行的键和一个 2TB 的输入源文件)上耗尽内存。

grep --no-group-separator -A 2 -B 1 -f key source

示例密钥文件

^CNACCCAAGGCTCATT  
^ANAGCGGCAACTCGCG  

我在每一行都添加了“^”,因为关键是在以“@”开头的行旁边的起始 16 个字符

图案由长度为 16 的字符 ATGCN 组成,它们是随机的。源文件中可能有多个匹配模式

对文件的示例搜索

@A00354:427:HVYWLDSXY:1:1101:1036:1000 1:N:0:ATTACTTC  
CNACCCAAGGCTCATTCATTATATAGTGGAGGCGGAGAACTTTCCTCCGGTTTGCCTAACATGCCAGCTGTCGGTGTCAAAACCGGCGGATCTCGGGAAGGGGGTCCTGAACTGTGCGTCTTAGGTCGATGGTAATAGGAGACGGGGGAC  
+  
:#:FFFFFF:F,FFFFFFF:FFF,FF:FFFFFF,FFFFFFFFFFFFFFFF:FFFF:FFFFFFFF:FFFFF,FFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:F,F:FFFFFFFFFFFFFF:F:F,:F:FFFFFFFFFFF:FFF  
@A00354:427:HVYWLDSXY:1:1101:1108:1000 1:N:0:ATTACTTC  
ANAGCGGCAACTCGCGGTTCCCCTACACATAGAAAACCTACGCCACATTATTGGCTAGGACGAGTGGTTCGTCTGCGTACGCAAGATTGTTGAGATCCACTATTGTCATTCAGTACTACGGTTCTTCTTATCTTGGTCGATCGTGTAAAA  
+  
F#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFF  
@A00354:427:HVYWLDSXY:1:1101:1271:1000 1:N:0:ATTACTTC  
CNATCCCGTCTCGAGCCCGCCCCAATAGCAACAACAACAACAACAACAACAACAACAGCAACAACACCAGCAACACCAGCAACAACAGCAACAACAACAACAGCAACAACAACAACAACAACAACAACAACAACAACAACAACAACAAGA  
+  
F#FFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFF:FFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  
@A00354:427:HVYWLDSXY:1:1101:1325:1000 1:N:0:ATTACTTC  
TNCGGTTCATAGGAATGTAGTCTTTGTAATTATGCGCAATTTCCAAACACTTCAAGGTTTTTTTGCAAATAAAACATTCAGGCCTCGTGTGTGCCGCTGCATCTTAGATCCAACGGCTCCTAGTTGCTCATATTCNACCCAAGGCTCATTAGGTGCTCCCCGTAGC  
+  
:#FFF:F,FFFFFFFFFFFF,:FFF::F,FFF,F:FFFFFFF:FFFF:FF:F:FFF:F:F:FFFFFFFF,FF,F:FF:FF::F,FFF:FFFFFF,:F::FFFFFFF:FF:FFFFF,FFFFFF,FFF:FFFFFFFFF,FFFF:FFFFFFF:  

即使我拆分密钥文件,它的速度也非常慢。

使用 perl one-liner 或 awk 可以更有效地完成吗?

预期的输出是

@A00354:427:HVYWLDSXY:1:1101:1036:1000 1:N:0:ATTACTTC CNACCCAAGGCTCATTCATTATATAGTGGAGGCGGAGAACTTTCCTCCGGTTTGCCTAACATGCCAGCTGTCGGTGTCAAAACCGGCGGATCTCGGGAAGGGGGTCCTGAACTGTGCGTCTTAGGTCGATGGTAATAGGAGACGGGGGAC + :#:FFFFFF:F,FFFFFFF:FFF,FF:FFFFFF,FFFFFFFFFFFFFFFF:FFFF:FFFFFFFF:FFFFF,FFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:F,F:FFFFFFFFFFFFFF:F:F,:F:FFFFFFFFFFF:FFF @A00354:427:HVYWLDSXY:1:1101:1108:1000 1:N:0:ATTACTTC ANAGCGGCAACTCGCGGTTCCCCTACACATAGAAAACCTACGCCACATTATTGGCTAGGACGAGTGGTTCGTCTGCGTACGCAAGATTGTTGAGATCCACTATTGTCATTCAGTACTACGGTTCTTCTTATCTTGGTCGATCGTGTAAAA + F#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

我看到了类似的代码

awk 'NR==FNR{a[$1]; next} {for (i in a) if (index($0, i)) print $1}' key source

它检查 key 中的每个条目是否是源的子字符串,但我无法动脑筋检查模式(^CNACCCAAGGCTCATT)并获取上一个。和下一行

我尝试过但无法识别的另一种方法是,zcat key | match each line against source file > output

*可能是因为我的代码变慢了,非常感谢任何替代方法

【问题讨论】:

  • 我怀疑使用 perl 或 awk 的脚本是否能在效率上超过 grep。如果 grep 不够快,请考虑ripgrep (rg),它比 grep 快很多。
  • 将其重构为 Perl 可能会有所帮助,因为它允许您将哈希存储在磁盘上(查找 tie)。我不认为它会特别优雅或快速,但可能会为内存耗尽问题提供一种解决方法。
  • @tripleee 是的,模式是以“@”开头的行之后的前 16 个字符。如果你能提供一个很好的工作示例,因为我不擅长编码
  • 你还能谈谈模式的分布(或者你称之为“键”)吗?假设您的字母表有五个不同的符号(TCGA 加 N)是否正确?模式是随机分布在 5^16 个可能值中还是可以概括?可能是edit 你的问题,而不是在 cmets 中隐藏细节。

标签: awk


【解决方案1】:

for (i in a) if (index($0, i)) 会非常慢,因为您在“搜索”文件的每行循环 100,000,000 次(因此 100M * 2TB 循环迭代!)它会产生不正确的输出为 @ 987654323@ 会在搜索行的任何位置而不是开头找到目标键,它必须是 index($0, i) == 1 才能仅在开头匹配。

这是在从“关键”文件行的开头删除所有 ^s 之后在 awk 中执行此操作的方法,因为我们将使用字符串进行有效的哈希查找,而不是像这样进行缓慢的正则表达式比较grep 需要,我们将对每行“源”进行 1 次哈希查找,而不是像您问题中的 awk 脚本中那样进行 100M 字符串比较:

$ cat tst.awk
NR==FNR { tgts[$1]; next }
c && !(--c) { print p3 ORS p2 ORS p1 ORS $0; f=0 }
{ key=substr($0,1,16); p3=p2; p2=p1; p1=$0 }
key in tgts { c=2 }

$ awk -f tst.awk key source
@A00354:427:HVYWLDSXY:1:1101:1036:1000 1:N:0:ATTACTTC
CNACCCAAGGCTCATTCATTATATAGTGGAGGCGGAGAACTTTCCTCCGGTTTGCCTAACATGCCAGCTGTCGGTGTCAAAACCGGCGGATCTCGGGAAGGGGGTCCTGAACTGTGCGTCTTAGGTCGATGGTAATAGGAGACGGGGGAC
+
:#:FFFFFF:F,FFFFFFF:FFF,FF:FFFFFF,FFFFFFFFFFFFFFFF:FFFF:FFFFFFFF:FFFFF,FFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:F,F:FFFFFFFFFFFFFF:F:F,:F:FFFFFFFFFFF:FFF
@A00354:427:HVYWLDSXY:1:1101:1108:1000 1:N:0:ATTACTTC
ANAGCGGCAACTCGCGGTTCCCCTACACATAGAAAACCTACGCCACATTATTGGCTAGGACGAGTGGTTCGTCTGCGTACGCAAGATTGTTGAGATCCACTATTGTCATTCAGTACTACGGTTCTTCTTATCTTGGTCGATCGTGTAAAA
+
F#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFF

有关c=2c && !(--c) 正在做什么的更多信息,请参见printing-with-sed-or-awk-a-line-following-a-matching-pattern/17914105#17914105,但它会设置行数的计数,然后当计数再次归零。

如果这超出了可用内存,请告知我们,因为另一种方法可能类似于以下伪代码(我不建议您在 shell 中执行此操作!):

sort keys
sort source by middle line keeping groups of 3 lines together
while !done; do
    read tgt < keys
    while read source_line; do
        key = substr(line,1,16)
        if key == tgt; then
            print line+context
        else if key > tgt; then
            break
        fi
    done < source
done

所以这个想法是你不要从“key”中读取下一个值,直到“source”中的当前值大于你正在使用的值。这会将内存使用量减少到接近于零,但它确实需要对两个输入文件进行排序。

【讨论】:

  • 第一个 awk oneliner 效果很好。虽然它不输出第 4 行,但我可以解决这个问题,所以无论如何它真的没关系。没有内存问题。 :) @Ed Morton
  • 我没有注意到有第 4 行,我以为只有 3 行。我将脚本更新为打印 4 行。
猜你喜欢
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多