Regex 如何删除与几个正则表达式模式之一匹配的行?

Regex 如何删除与几个正则表达式模式之一匹配的行?,regex,unix,awk,sed,pattern-matching,Regex,Unix,Awk,Sed,Pattern Matching,我有一个制表符分隔的文本文件,希望有效地删除满足以下任一条件的整行: ALT列中等于 NA00001列和后续列中的值,这些值在两个分隔符或/之前和之后具有相同的数字,例如0 | 0,1 | 1,2/2等 下面是一个示例输入文件: CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NA00001 NA00002 NA00003 20

我有一个制表符分隔的文本文件,希望有效地删除满足以下任一条件的整行:

  • ALT
    列中等于
  • NA00001
    列和后续列中的值,这些值在两个分隔符
    /
    之前和之后具有相同的数字,例如
    0 | 0
    1 | 1
    2/2
下面是一个示例输入文件:

CHROM POS     ID        REF ALT    QUAL FILTER INFO                              FORMAT      NA00001        NA00002        NA00003
20     14370   rs6054257 G      A       29   PASS   NS=3;DP=14;AF=0.5;DB;H2           GT:GQ:DP:HQ 0|0:48:1:51,51 0|0:48:8:51,51 1/1:43:5:.,.
20     17330   .         T      A       3    q10    NS=3;DP=11;AF=0.017               GT:GQ:DP:HQ 0|0:49:3:58,50 0|1:3:5:65,3   0/0:41:3
20     1110696 rs6040355 A      G,T     67   PASS   NS=2;DP=10;AF=0.333,0.667;AA=T;DB GT:GQ:DP:HQ 1|2:21:6:23,27 2|1:2:0:18,2   2/2:35:4
20     1110696 rs6040360 A      .     67   PASS   NS=2;DP=10;AF=0.333,0.667;AA=T;DB GT:GQ:DP:HQ 1|2:21:6:23,27 2|1:2:0:18,2   2/2:35:4
示例输出文件为:

CHROM POS     ID        REF ALT    QUAL FILTER INFO                              FORMAT      NA00001        NA00002        NA00003
20     17330   .         T      A       3    q10    NS=3;DP=11;AF=0.017               GT:GQ:DP:HQ 0|0:49:3:58,50 0|1:3:5:65,3   0/0:41:3
20     1110696 rs6040355 A      G,T     67   PASS   NS=2;DP=10;AF=0.333,0.667;AA=T;DB GT:GQ:DP:HQ 1|2:21:6:23,27 2|1:2:0:18,2   2/2:35:4

您的示例似乎不包括任何符合“在
ALT
列中的值等于
”标准的行,或不符合第二个标准的行(标题行除外)。因此,我在您的示例中添加了我自己的一些行以进行测试;我希望我已经理解了你的标准

如果我们在awk脚本中使用类似于awk:
$5==“{next}
的东西,那么通过测试特定字段可以很容易地匹配第一个标准。仅仅使用正则表达式也很简单:
^I[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I
,其中
^I
是一个制表符,在第五个(ALT)字段中只与“.”匹配行

对于严格的正则表达式,您不能直接表示“分隔符前后的同一个数字”。您必须使用特定值替换子表达式:
0[|/]0 | 1[|/]1 | 2[|/]2
。。。但是只有10个数字,所以这并不是特别麻烦。例如,您可以使用一个长的egrep命令行进行过滤:

egrep -v '^[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I|0[|/]0|1[|/]1|2[|/]2|3[|/]3|4[|/]4|5[|/]5|6[|/]6|7[|/]7|8[|/]8|9[|/]9' input-file
很明显,这不是您希望定期手动键入的内容,也不适合维护。一点awk脚本更好:

#! /usr/bin/awk -f
# Skip lines with "." in the fifth (ALT) field
$5 == "." {next}
# Skip lines with the same digit before and after the delimiter in any field
/0[|/]0/ {next}
/1[|/]1/ {next}
/2[|/]2/ {next}
/3[|/]3/ {next}
/4[|/]4/ {next}
/5[|/]5/ {next}
/6[|/]6/ {next}
/7[|/]7/ {next}
/8[|/]8/ {next}
/9[|/]9/ {next}

# Copy all other lines to the output
{print}
为了可读性,我将单独的数字检查作为单独的awk语句

使用扩展正则表达式(ERE),可以使用反向引用直接表示“分隔符前后的相同字符”。应谨慎使用反向参考,因为它们可能会产生病理表现特征;当然,您必须使用支持它们的语言,如perl。POSIX awk和Gnu呆呆地看不见。下面是处理第二个标准的Perl one liner:

LINE: while (<STDIN>) { next LINE if /(\d)[|\/]\g1/; print }
LINE:while(){下一行if/(\d)[| \/]\g1/;print}

这可能不是很好的Perl——我几乎从未使用过这种语言——但它在我的测试中起作用。
(\d)
匹配并记住分隔符前的数字,
\g1
匹配记住的分隔符后的数字。

您的示例似乎没有包含任何符合“ALT列中等于
标准的值”的行,或不符合第二个标准的行(标题行除外)。因此,我在您的示例中添加了我自己的一些行以进行测试;我希望我已经理解了你的标准

perl -alnE '$F[4]  eq "."           and
            $F[9] =~ m!(\d)[|/]\1!  and
            $F[10] =~ m!(\d)[|/]\1! and
            say'
如果我们在awk脚本中使用类似于awk:
$5==“{next}
的东西,那么通过测试特定字段可以很容易地匹配第一个标准。仅仅使用正则表达式也很简单:
^I[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I
,其中
^I
是一个制表符,在第五个(ALT)字段中只与“.”匹配行

对于严格的正则表达式,您不能直接表示“分隔符前后的同一个数字”。您必须使用特定值替换子表达式:
0[|/]0 | 1[|/]1 | 2[|/]2
。。。但是只有10个数字,所以这并不是特别麻烦。例如,您可以使用一个长的egrep命令行进行过滤:

egrep -v '^[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I|0[|/]0|1[|/]1|2[|/]2|3[|/]3|4[|/]4|5[|/]5|6[|/]6|7[|/]7|8[|/]8|9[|/]9' input-file
很明显,这不是您希望定期手动键入的内容,也不适合维护。一点awk脚本更好:

#! /usr/bin/awk -f
# Skip lines with "." in the fifth (ALT) field
$5 == "." {next}
# Skip lines with the same digit before and after the delimiter in any field
/0[|/]0/ {next}
/1[|/]1/ {next}
/2[|/]2/ {next}
/3[|/]3/ {next}
/4[|/]4/ {next}
/5[|/]5/ {next}
/6[|/]6/ {next}
/7[|/]7/ {next}
/8[|/]8/ {next}
/9[|/]9/ {next}

# Copy all other lines to the output
{print}
为了可读性,我将单独的数字检查作为单独的awk语句

使用扩展正则表达式(ERE),可以使用反向引用直接表示“分隔符前后的相同字符”。应谨慎使用反向参考,因为它们可能会产生病理表现特征;当然,您必须使用支持它们的语言,如perl。POSIX awk和Gnu呆呆地看不见。下面是处理第二个标准的Perl one liner:

LINE: while (<STDIN>) { next LINE if /(\d)[|\/]\g1/; print }
LINE:while(){下一行if/(\d)[| \/]\g1/;print}

这可能不是很好的Perl——我几乎从未使用过这种语言——但它在我的测试中起作用。
(\d)
匹配并记住分隔符前的数字,
\g1
匹配记住的分隔符后的数字。

您的示例似乎没有包含任何符合“ALT列中等于
标准的值”的行,或不符合第二个标准的行(标题行除外)。因此,我在您的示例中添加了我自己的一些行以进行测试;我希望我已经理解了你的标准

perl -alnE '$F[4]  eq "."           and
            $F[9] =~ m!(\d)[|/]\1!  and
            $F[10] =~ m!(\d)[|/]\1! and
            say'
如果我们在awk脚本中使用类似于awk:
$5==“{next}
的东西,那么通过测试特定字段可以很容易地匹配第一个标准。仅仅使用正则表达式也很简单:
^I[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I
,其中
^I
是一个制表符,在第五个(ALT)字段中只与“.”匹配行

对于严格的正则表达式,您不能直接表示“分隔符前后的同一个数字”。您必须使用特定值替换子表达式:
0[|/]0 | 1[|/]1 | 2[|/]2
。。。但是只有10个数字,所以这并不是特别麻烦。例如,您可以使用一个长的egrep命令行进行过滤:

egrep -v '^[^^I]*^I[^^I]*^I[^^I]*^I[^^I]*^I\.^I|0[|/]0|1[|/]1|2[|/]2|3[|/]3|4[|/]4|5[|/]5|6[|/]6|7[|/]7|8[|/]8|9[|/]9' input-file
很明显,这不是您希望定期手动键入的内容,也不适合维护。一点awk脚本更好:

#! /usr/bin/awk -f
# Skip lines with "." in the fifth (ALT) field
$5 == "." {next}
# Skip lines with the same digit before and after the delimiter in any field
/0[|/]0/ {next}
/1[|/]1/ {next}
/2[|/]2/ {next}
/3[|/]3/ {next}
/4[|/]4/ {next}
/5[|/]5/ {next}
/6[|/]6/ {next}
/7[|/]7/ {next}
/8[|/]8/ {next}
/9[|/]9/ {next}

# Copy all other lines to the output
{print}
我已经把每个数字放在ch上了