返回文本行中多个字符串匹配的多个位置

Returning multiple positions of multiple string matches within lines of text

我有一个文本文件,例如:

>HiC_scaffold_1 LN:i:45809557 RC:i:152227 XC:f:0.987707
CAGGAAAGCCGCGTAAGTGAATATATGCAGCAACCTACCGAAAAGTGGGCCAATCCAACCAATCTTGCTTGCACAATGGAAAGAGCCACTGGTTTATCTCTCCATCGAATCAAATTAGCCAAAGGTGTGCGTTCATGAGCCCATGCTAAAGTTTCAATCAATTCTTGCCAATATCCACGCCAGGAAATTAAGAACATAAATCCAGTGCTGCAGC
>HiC_scaffold_2 LN:i:32008785 RC:i:102679 XC:f:0.981906
AAAGCTGCCCCTAGGCCGAACAAAATGGTCGGATGCGAAGAGAAATTGTTTGGCTCAAAATTTTACGAGCTTGTGCAGAACTTCAAGGCAATCATATCGGCAGGTGACACGAAGTGATTCGAGTTCGGCAGCTTTGCCCCTCCTTTTTCCTTGACGAAAGATAACTTTTTCTGAAAATAACACGTGCCCCGATTCCGGCCGAAATGACTCGAAT
>HiC_scaffold_3 LN:i:26569524 RC:i:79397 XC:f:0.996709
CCTAAACCCTAAACCCTAAACCCCTAAACCCTAAACCCTAACCCTAAACCCTAAACCCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAAACCCACCTAAACCCTAAACCCTAAACCCCCTAAACCCAAAACGCTGCCCCTAAACCCTAAACCCTAAACCCGCAGCTAACCCTAAACC

我想在不以“>”开头的行中找到模式“GCAGC”或“GCTGC”出现的位置。

有没有办法使用 sedawk 来 return 匹配的行号,以及每行中匹配的索引(即,数字每行中每个匹配项开始的字符数)?

谢谢!

假设您的数据在文件 data.txt 中,简单的解决方案是:

awk 'BEGIN {RS="\n>";} \
  { for (j=1;j<=length($i)-4;j++) { \
    if (substr($i,j,5) == "GCAGC") { \
      print "entry " NR " column " j ": GCAGC" \
    } else if(substr($i,j,5) == "GCTGC") { \
      print "entry " NR " column " j ": GCTGC" \
    } \
  } \
}' data.txt

这里我假设您的条目由“\n>”分隔并打印条目编号,因为我假设这就是您想要的。否则你可以省略第一部分并简单地 运行

awk '{ for (j=1;j<=length($i)-4;j++) { \
    if (substr($i,j,5) == "GCAGC") { \
      print "line " NR " column " j ": GCAGC" \
    } else if(substr($i,j,5) == "GCTGC") { \
      print "line " NR " column " j ": GCTGC" \
    } \
  } \
}' data.txt

这给了你行号。有关详细信息,请参阅 documentation.

这是一个处理重叠模式的解决方案:

BEGIN {
    patternRegex = "GCAGC|GCTGC"
}

/^[^>]/ {
    offset = 0
    target = [=10=]
    match(target, patternRegex)
    while (RSTART > 0) {
        matchedString = substr(target, RSTART, RLENGTH)
        printf "line %d: %s at position %d\n", NR, matchedString, offset + RSTART
        offset += RSTART + length("CG*") - 1
        target = substr(target, RSTART + length("CG*"))
        match(target, patternRegex)
    }
}

如果脚本存储在 find-patterns.awk 中,输入在 input.txt 中,我们得到以下输出:

$ awk -f find-patterns.awk < input.txt 
line 2: GCAGC at position 27
line 2: GCTGC at position 207
line 2: GCAGC at position 210
line 4: GCTGC at position 4
line 4: GCAGC at position 128
line 6: GCTGC at position 169
line 6: GCAGC at position 198

假设如果可能出现重叠的目标字符串,您想了解所有这些,这将在每个 Unix 机器上的任何 shell 中使用任何 awk 工作:

$ cat tst.awk
!/^>/ {
    while ( match([=10=],/GC[AT]GC/) ) {
        print NR, RSTART, substr([=10=],RSTART,RLENGTH)
        [=10=] = substr([=10=],1,RSTART-1) " " substr([=10=],RSTART+1)
    }
}

$ awk -f tst.awk file
2 27 GCAGC
2 207 GCTGC
2 210 GCAGC
4 4 GCTGC
4 128 GCAGC
6 169 GCTGC
6 198 GCAGC
7 4 GCAGC
7 7 GCAGC
7 10 GCAGC

以上是此输入文件的 运行:

$ cat file
>HiC_scaffold_1 LN:i:45809557 RC:i:152227 XC:f:0.987707
CAGGAAAGCCGCGTAAGTGAATATATGCAGCAACCTACCGAAAAGTGGGCCAATCCAACCAATCTTGCTTGCACAATGGAAAGAGCCACTGGTTTATCTCTCCATCGAATCAAATTAGCCAAAGGTGTGCGTTCATGAGCCCATGCTAAAGTTTCAATCAATTCTTGCCAATATCCACGCCAGGAAATTAAGAACATAAATCCAGTGCTGCAGC
>HiC_scaffold_2 LN:i:32008785 RC:i:102679 XC:f:0.981906
AAAGCTGCCCCTAGGCCGAACAAAATGGTCGGATGCGAAGAGAAATTGTTTGGCTCAAAATTTTACGAGCTTGTGCAGAACTTCAAGGCAATCATATCGGCAGGTGACACGAAGTGATTCGAGTTCGGCAGCTTTGCCCCTCCTTTTTCCTTGACGAAAGATAACTTTTTCTGAAAATAACACGTGCCCCGATTCCGGCCGAAATGACTCGAAT
>HiC_scaffold_3 LN:i:26569524 RC:i:79397 XC:f:0.996709
CCTAAACCCTAAACCCTAAACCCCTAAACCCTAAACCCTAACCCTAAACCCTAAACCCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAAACCCACCTAAACCCTAAACCCTAAACCCCCTAAACCCAAAACGCTGCCCCTAAACCCTAAACCCTAAACCCGCAGCTAACCCTAAACC
fooGCAGCAGCAGCbar

perl(不重叠):

$ perl -lne 'if(!/^>/){print join " ", $., $-[0]+1, $& while /GCAGC|GCTGC/g}' ip.txt
2 27 GCAGC
2 207 GCTGC
4 4 GCTGC
4 128 GCAGC
6 169 GCTGC
6 198 GCAGC
  • if(!/^>/) 行不以 >
  • 开头
  • $. 给出行号
  • $-[0]给出了匹配的起始位置(从0开始的索引,所以添加了1
  • $& 有匹配的部分
  • join " " 使用 space 作为分隔符来组合所需的值
  • while /GCAGC|GCTGC/g 遍历给定正则表达式的所有匹配项

要同时匹配重叠的情况,请将正则表达式更改为 /(?=(GCAGC|GCTGC))/g,以便要匹配的字符串现在位于先行捕获组中。这将尝试在不消耗字符的情况下在每个位置进行匹配,并且匹配的部分将从 </code> 中获得。如果搜索词本身重叠(例如:<code>ABCABCD),则交替中最左边的词优先。

$ perl -lne 'if(!/^>/){print join " ", $., $-[0]+1,  while /(?=(GCAGC|GCTGC))/g}' ip.txt
2 27 GCAGC
2 207 GCTGC
2 210 GCAGC
4 4 GCTGC
4 128 GCAGC
6 169 GCTGC
6 198 GCAGC

使用 ripgrep,这可能比其他解决方案更快。但缺点是这不会过滤掉以 > 开头的行,并且仅适用于非重叠情况:

$ rg --vimgrep -o --no-filename 'GCAGC|GCTGC' ip.txt
2:27:GCAGC
2:207:GCTGC
4:4:GCTGC
4:128:GCAGC
6:169:GCTGC
6:198:GCAGC
  • --vimgrep 专为与 vim 一起使用而设计,它给出行号和列号
  • -o 只得到匹配的部分而不是整行
  • --no-filename 避免输出中的文件名前缀
  • 如果你想要 space 分隔符而不是 : 字符,请使用 --field-match-separator=' '

这可能对你有用(GNU sed 和 ripgrep):

sed '/>/g' file | rg --column -o 'GC[AT]GC'| sed 'y/:/ /'

包含 > 的空行,使用 ripgrep 完成大部分工作并使用最终的 sed 调用清理结果。

选择:

rg --column -o '>|GC[AT]GC' file | sed -E 'y/:/ /;/>/h;G;/^(\S* ).*\n/!P;d'

感谢 Sundeep。