awk NR==FNR для синтаксиса команды

У меня возникли проблемы с использованием awk NR==FNR для возврата интересующих строк из входного файла .fastq.

У меня есть следующий пример входного файла с именем example.fastq

@SRR1111111.1 1/1
CTGGANAAGTGAAATAATATAAATTTTTCCACTATTGAATAAAAGCAACTTAAATTTTCTAAGTCG
+
AAAAA#EEEEEEEEEEEEEEEEEEEEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEA<AAEEEEE<6
@SRR1111111.2 2/1
CTATANTATTCTATATTTATTCTAGATAAAAGCATTCTATATTTAGCATATGTCTAGCAAAAAAAA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6
@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6

Я пытаюсь извлечь группы из четырех строк, которые содержат интересующую строку, важно, чтобы были разрешены приблизительные совпадения, поэтому вместо grep используется agrep. Пример ниже работает.

agrep -1 -n "GAAATAATA" example.fastq | awk -F: 'NR==FNR{for(i=($1-1);i<=($1+2);i++)a[i];next}FNR in a' - example.fastq

Приведенная выше команда выдает следующий правильный вывод.

@SRR1111111.1 1/1
CTGGANAAGTGAAATAATATAAATTTTTCCACTATTGAATAAAAGCAACTTAAATTTTCTAAGTCG
+
AAAAA#EEEEEEEEEEEEEEEEEEEEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEA<AAEEEEE<6
@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6

Однако, если я использую последовательность, не содержащуюся во второй строке, эта команда все равно печатает две верхние строки, как в следующем примере.

agrep -1 -n "TAGATAAAACT" example.fastq | awk -F: 'NR==FNR{for(i=($1-1);i<=($1+2);i++)a[i];next}FNR in a' - example.fastq

@SRR1111111.1 1/1
CTGGANAAGTGAAATAATATAAATTTTTCCACTATTGAATAAAAGCAACTTAAATTTTCTAAGTCG
@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6

Спасибо, что помогли мне понять поведение этой команды awk.


person Paul    schedule 17.12.2018    source источник
comment
Команда не имеет смысла, поэтому нет никакого способа помочь вам понять ее. На что вы надеетесь, что установка FS на :, а затем выполнение for(i=($1-1);i<=($1+2);i++)a[i] сделает для вас? Если вы все еще пытаетесь сделать то, о чем был ваш последний вопрос, просто используйте сценарий, который я там предоставил: stackoverflow.com/a/53771992 /1745001. Также см. stackoverflow.com/help/someone-answers.   -  person Ed Morton    schedule 18.12.2018


Ответы (3)


Вы можете использовать это agrep + awk решение:

srch() {
   awk -F ': ' 'NR==FNR {
      a[$1] = 1
      next
   }
   a[FNR] {
      print p
      print
      for (i=0; i<2 && getline > 0; i++)
         print
   }
   {
      p=$0
   }' <(agrep -1 -n "$2" "$1") "$1"
}

Then run it as:

srch file 'GAAATAATA'

@SRR1111111.1 1/1
CTGGANAAGTGAAATAATATAAATTTTTCCACTATTGAATAAAAGCAACTTAAATTTTCTAAGTCG
+
AAAAA#EEEEEEEEEEEEEEEEEEEEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEA<AAEEEEE<6
@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6

and this:

srch file 'TAGATAAAACT

@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6'
person anubhava    schedule 17.12.2018
comment
Я думаю, что у вас там бродячий r=NR;. Вы никогда не читали это. - person Adam Katz; 18.12.2018
comment
Спасибо, но это решение не способствует приблизительным совпадениям. Есть ли способ встроить функциональность приблизительного совпадения, чтобы одно несоответствие по-прежнему возвращало результат? - person Paul; 18.12.2018
comment
Мой плохой я должен был сделать это более ясным. Я отредактирую, чтобы уточнить. - person Paul; 18.12.2018

В вашем вводе нет двоеточий (:), поэтому $1 относится ко всей строке, а ($1-1) и ($2+2) будут -1 и 2 соответственно, что означает, что ваш цикл for всегда будет выполняться ровно четыре раза (для значений i, равных -1, 0, 1, затем 2).

Внутри цикла for вы гарантируете, что a[i] существует (это a[-1], a[0], a[1] и a[2]).

Последняя часть вашего кода печатает проверяемую в это время строку (но не из первого файла благодаря next в предыдущей строфе) всякий раз, когда массив a содержит запись для номера строки этого файла. Следовательно, он печатает строки 1 и 2 из каждого входа (поскольку a[FNR] существует для FNR, равного 1 или 2).

Поскольку вам нужен приблизительный ответ и, следовательно, вы должны использовать agrep, идея, предложенная ответом Джеймса Брауна на ваш другой вопрос, имеет смысл, но его реализация (как описано выше) не работает.

Следующее решение использует попадания agrep в качестве подсказок для окружающих строк для печати рядом с попаданиями (agrep не поддерживает контекстные строки, такие как -A NUM и -B num grep, иначе мы могли бы сделать agrep -A1 -B2 -1 -n PATTERN example.fastq для более простого ответа).

agrep -1 "GAAATAATA" example.fastq | awk '
  NR == FNR { agrep_hit[$0] = 1; next }
  agrep_hit[$0] { print last_line; i = 1 }       
  0 < i && i < 4 { i++; print } 
  { last_line = $0 }
' - example.fastq

Это проверяет входной файл дважды. Первый раз использует agrep для поиска приблизительных совпадений с шаблоном, а второй использует awk для получения запрошенных строк контекста.

Когда общий номер строки в awk (NR) равен номеру строки локального файла (FNR), это означает, что мы проверяем первый ввод (-, стандартный ввод, который является выводом agrep). Мы сохраняем приблизительные совпадения шаблонов в ассоциативном массиве на потом, а затем переходим к следующей строке с помощью next (поэтому остальные команды awk работают только с более поздними входными данными).

Поскольку вам нужна предыдущая строка, мы должны напечатать ее явно. Последняя строфа кода awk сохраняет текущую строку как last_line, чтобы мы могли получить ее позже. В строке, которая была выведена agrep (и, таким образом, сохранена в нашем массиве), мы печатаем это сохраненное last_line и устанавливаем итератор i на 1.

Когда i равно 1, 2 или 3, мы увеличиваем его и печатаем текущую строку. Это напечатает совпадающую строку, а затем еще две для контекста.

person Adam Katz    schedule 17.12.2018
comment
Спасибо за этот полезный разбор синтаксиса команд awk. Использование agrep в вопросе заключается в разрешении несоответствий, которые невозможны с помощью grep. К сожалению, у agrep нет аргументов -B (до) и -A (после), поэтому в качестве обходного пути используется awk. Пожалуйста, смотрите этот связанный пост для получения более подробной информации по этой проблеме. stackoverflow.com/questions/53768447 / - person Paul; 18.12.2018
comment
Я пересмотрел свое решение, чтобы отразить необходимость приблизительного соответствия шаблону agrep, и включил вашу ссылку на источник этого кода в свой ответ. Пожалуйста, включите такие ссылки в ваши будущие вопросы. - person Adam Katz; 18.12.2018
comment
Хотя я следую логике и ценю объяснение, это решение не дает мне никаких результатов. Строки 1 и 2 работают ./agrep/agrep -1 -n "GAAATAATA" example.fastq | awk 'NR==FNR { agrep_hit[$0] = 1; print }' - example.fastq, но я не могу получить результат, добавляя дополнительные строки. - person Paul; 18.12.2018
comment
Извините, я полагал, что ваша команда agrep была более разумной; ты заставил меня apt install agrep. Просто удалите -n, и он будет работать (номера строк с префиксом не позволяли сопоставлять строки из первого чтения файла со строками из второго чтения файла). Я обновил свой ответ на этот счет. - person Adam Katz; 18.12.2018

с определением разделителя записей (GNU awk)

$ awk -v RS='(^|\n)@' '/GAAATAATA/{printf "%s", rt $0} {rt=RT}' file

@SRR1111111.1 1/1
CTGGANAAGTGAAATAATATAAATTTTTCCACTATTGAATAAAAGCAACTTAAATTTTCTAAGTCG
+
AAAAA#EEEEEEEEEEEEEEEEEEEEEEEAEEEEEEEEEEEEEEEEEEEEEEEEEA<AAEEEEE<6
@SRR1111111.3 3/1
CTATANTATTGAAATAATAATGTAGATAAAACTATTGAATAACAGCAACTTAAATTTTCAATAAGA
+
AAAAA#EE6EEEEEEEEEEEEAAEEAEEEEEEEEEEEE/EAE/EAE/EA/EAEAAAE//EEAEAA6
person karakfa    schedule 17.12.2018