Фильтрация столбца с awk и регулярным выражением

У меня довольно простой вопрос. У меня есть файл, содержащий несколько столбцов, и я хочу их фильтровать с помощью awk.

Итак, интересующий столбец - это 6-й столбец, и я хочу найти каждую строку, содержащую:

  • начиная с номера от 1 до 100
  • после этого один "S" или "M"
  • снова число от 1 до 100
  • после этого один "S" или "M"

Итак, пример: 20S50M в порядке

Я пробовал:

awk '{ if($6 == '/[1-100][S|M][1-100][S|M]/') print} file.txt

но это не сработало... Что я делаю неправильно?

Ответ 1

Это должно сделать трюк:

awk '$6~/^(([1-9]|[1-9][0-9]|100)[SM]){2}$/' file

Regexplanation:

^                        # Match the start of the string
(([1-9]|[1-9][0-9]|100)  # Match a single digit 1-9 or double digit 10-99 or 100
[SM]                     # Character class matching the character S or M
){2}                     # Repeat everything in the parens twice
$                        # Match the end of the string

У вас довольно много проблем с вашим выражением:

awk '{ if($6 == '/[1-100][S|M][1-100][S|M]/') print} file.txt
  • == - оператор сравнения строк. Оператор сравнения регулярных выражений ~.
  • Вы не цитируете строки регулярных выражений (вы никогда ничего не цитируете с одинарными кавычками в awk рядом с самим script), а ваш script не имеет окончательной (юридической) одинарной кавычки.
  • [0-9] - это класс символов для цифровых символов, это не числовой диапазон. Это означает совпадение с любым символом в классе 0,1,2,3,4,5,6,7,8,9 не любое числовое значение внутри диапазона, поэтому [1-100] не является регулярным выражением для цифр в числовом диапазоне 1 - 100, оно будет соответствовать либо 1, либо 0.
  • [SM] эквивалентен (S|M) то, что вы пробовали [S|M], совпадает с (S|\||M). Вам не нужен оператор OR в классе символов.

Awk, используя следующую структуру condition{action}. Если условие True, действия в следующем блоке {} выполняются для текущей текущей записи. Условием в моем решении является $6~/^(([1-9]|[1-9][0-9]|100)[SM]){2}$/, которое можно прочитать так же, как шестой столбец соответствует регулярному выражению, если True печатается строка, потому что если вы не получите никаких действий, то awk выполнит {print $0} по умолчанию.

Ответ 2

Я бы выполнил проверку регулярного выражения и числовую проверку как разные шаги. Этот код работает с GNU awk:

$ cat data
a b c d e 132x123y
a b c d e 123S12M
a b c d e 12S23M
a b c d e 12S23Mx

Мы ожидаем, что только 3-я строка пройдет проверку

$ gawk '
    match($6, /^([[:digit:]]{1,3})[SM]([[:digit:]]{1,3})[SM]$/, m) && 
    1 <= m[1] && m[1] <= 100 && 
    1 <= m[2] && m[2] <= 100 {
        print
    }
' data
a b c d e 12S23M

Для удобства обслуживания вы можете инкапсулировать это в функцию:

gawk '
    function validate6() {
        return( match($6, /^([[:digit:]]{1,3})[SM]([[:digit:]]{1,3})[SM]$/, m) && 
                1<=m[1] && m[1]<=100 && 
                1<=m[2] && m[2]<=100 );
    }
    validate6() {print}
' data

Ответ 3

Регулярные выражения не могут проверять числовые значения. "Число от 1 до 100" находится вне того, что могут делать регулярные выражения. Что вы можете сделать, это проверить "1-3 цифры".

Вы хотите что-то вроде этого

/\d{1,3}[SM]\d{1,3}[SM]/

Обратите внимание, что класс символов [SM] не имеет символа чередования !. Вам понадобится только это, если вы пишете его как (S|M).

Ответ 4

Способ записи script вы отправили:

awk '{ if($6 == '/[1-100][S|M][1-100][S|M]/') print} file.txt

в awk, чтобы он делал то, что вы пытаетесь сделать SEEM:

awk '$6 ~ /^(([1-9][0-9]?|100)[SM]){2}$/' file.txt

Поместите некоторый пример ввода и ожидаемый результат, чтобы помочь нам помочь вам.

Ответ 5

Попробуйте следующее:

awk '$ 6 ~/^ ([1-9] | 0 [1-9] | [1-9] [0-9] | 100) + [S | M] + ([1-9] | 0 [1-9] | [1-9] [0-9] | 100) + [S | M] $/'file.txt

Поскольку вы точно не указали, как будет выглядеть форматирование в столбце 6, вышесказанное будет работать там, где столбец выглядит как "03M05S", "40S100M" или "3M5S"; и исключить все остальное. Например, он не найдет "03F05S", "200M05S", "03M005S, 003M05S" или "003M005S".

Если вы можете сохранить цифры в столбце 6 до двух, когда 0-99, или три, когда ровно 100, что означает ровно один начальный ноль, когда меньше 10, а в противном случае нет начальных нулей, то это более простое совпадение. Вы можете использовать приведенный выше шаблон, но исключать отдельные цифры (удалить первое условие [1-9]), например

awk '$ 6 ~/^ (0 [1-9] | [1-9] [0-9] | 100) + [S | M] + (0 [1-9] | [1-9] [0-9] | 100) + [S | M] $/'file.txt

Ответ 6

Я знаю, что на этот поток уже получен ответ, но на самом деле у меня есть похожая проблема (связанная с поиском строк, которые "используют запрос"). Я пытаюсь суммировать все целые числа, предшествующие символу, как 'S', 'M', 'I', '=', 'X', 'H', чтобы найти длину чтения через парный конец читать строку СИГАРА.

Я написал скрипт Python, который берет в столбце $ 6 из файла SAM/BAM:

import sys                      # getting standard input
import re                       # regular expression module

lines = sys.stdin.readlines()   # gets all CIGAR strings for each paired-end read
total = 0
read_id = 1                     # complements id from filter_1.txt

# Get an int array of all the ints matching the pattern 101M, 1S, 70X, etc.
# Example inputs and outputs: 
# "49M1S" produces total=50
# "10M757N40M" produces total=50

for line in lines:
    all_ints = map(int, re.findall(r'(\d+)[SMI=XH]', line))
    for n in all_ints:
        total += n
    print(str(read_id)+ ' ' + str(total))
    read_id += 1
    total = 0

Цель read_id - пометить каждую прочитанную операцию как "уникальную", если вы хотите взять read_lengths и распечатать их рядом со столбцами awk-ed из файла BAM.

Я надеюсь, что это помогает или, по крайней мере, помогает следующему пользователю, у которого есть подобная проблема. Я обратился к fooobar.com/info/208406/... за справкой.