Возврат нескольких позиций нескольких совпадений строк в строках текста

#awk #sed #grep

Вопрос:

У меня есть текстовый файл, такой как:

 >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».

Есть ли способ использовать sed или awk возвращать оба номера строк совпадений, а также индексы совпадений в каждой строке (т. Е. Количество символов в каждой строке, с которых начинается каждое совпадение)?

Спасибо!

Комментарии:

1. Могут ли целевые строки перекрываться? Каким должен быть вывод для входной строки fooGCAGCAGCAGCbar ?

Ответ №1:

Предполагая, что если могут возникать перекрывающиеся целевые строки, вы хотите знать обо всех из них, это будет работать с использованием любого awk в любой оболочке на каждом блоке Unix:

 $ cat tst.awk
!/^>/ {
    while ( match($0,/GC[AT]GC/) ) {
        print NR, RSTART, substr($0,RSTART,RLENGTH)
        $0 = substr($0,1,RSTART-1) " " substr($0,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
 

Комментарии:

1. Потрясающе, большое спасибо. Вероятно, все три из этих решений работают, но я попробовал это из полного файла, и, похоже, оно работает идеально.

Ответ №2:

С perl (неперекрывающимися):

 $ perl -lne 'if(!/^>/){print join " ", $., $-[0] 1, $amp; 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 что добавляется)
  • $amp; имеет совпадающую часть
  • join " " используйте пробел в качестве разделителя для объединения требуемых значений
  • while /GCAGC|GCTGC/g переберите все совпадения для данного регулярного выражения

Чтобы также сопоставить перекрывающиеся случаи, измените регулярное /(?=(GCAGC|GCTGC))/g выражение так, чтобы соответствующие строки теперь находились внутри группы захвата смотровой площадки. Это будет пытаться соответствовать в каждой позиции, не потребляя символов, и соответствующая часть будет доступна из $1 . Если сами поисковые запросы перекрываются (например: ABC и ABCD ), то тот термин, который является крайним левым в чередовании, получает приоритет.

 $ perl -lne 'if(!/^>/){print join " ", $., $-[0] 1, $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 чтобы избежать префикса имени файла в выводе
  • Используйте --field-match-separator=' ' , если вам нужен разделитель пробелов вместо : символа

Ответ №3:

Предполагая , что ваши данные находятся в файле 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
 

что дает вам номера строк. Для получения дополнительной информации см. документацию.

Комментарии:

1. Вы должны упомянуть, что для первого скрипта требуется GNU awk для многозначных RS, он не будет работать с POSIX awk.

Ответ №4:

Вот решение, которое обрабатывает перекрывающиеся шаблоны:

 BEGIN {
    patternRegex = "GCAGC|GCTGC"
}

/^[^>]/ {
    offset = 0
    target = $0
    match(target, patternRegex)
    while (RSTART > 0) {
        matchedString = substr(target, RSTART, RLENGTH)
        printf "line %d: %s at position %dn", 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
 

Ответ №5:

Это может сработать для вас (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* ).*n1/!P;d'
 

Слава Сандипу.