23

我有一个很简单的问题。我有一个包含几列的文件,我想使用 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

但它没有用......我做错了什么?

4

6 回答 6

52

这应该可以解决问题:

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

正则说明:

^                        # 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)并且您的脚本缺少最终(合法)单引号。
  • [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}

于 2013-09-23T14:42:51.353 回答
2

正则表达式无法检查数值。“从 1 到 100 的数字”超出了正则表达式的功能。您可以做的是检查“1-3 位数”。

你想要这样的东西

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

请注意,字符类[SM]没有!交替字符。仅当您将其编写为(S|M).

于 2013-09-23T14:42:26.877 回答
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
于 2013-09-23T16:21:43.227 回答
1

编写您发布的脚本的方式:

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

在 awk 所以它会做你似乎想要做的事情是:

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

发布一些示例输入和预期输出,以帮助我们为您提供更多帮助。

于 2013-09-23T16:28:06.990 回答
0

试试这个:

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”。

如果您可以在 0-99 时将第 6 列中的数字保留为两位,或者在恰好 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
于 2013-09-23T18:20:52.833 回答
0

我知道这个线程已经得到回答,但我实际上有一个类似的问题(与查找“使用查询”的字符串有关)。我正在尝试将“S”、“M”、“I”、“=”、“X”、“H”等字符前面的所有整数相加,以通过配对端找到读取长度读取 CIGAR 字符串。

我编写了一个 Python 脚本,它从 SAM/BAM 文件中获取 $6 列:

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 并将它们打印在 BAM 文件中的 awk 列旁边。

我希望这会有所帮助,或者至少可以帮助下一个遇到类似问题的用户。我咨询了https://stackoverflow.com/a/11339230以供参考。

于 2019-07-29T21:41:18.330 回答