有没有办法在 awk match() 函数 'match([=10=],/r{0,var}/)' 中使用变量来定义范围

Is there a way to use a variable in to define range in awk match() function 'match($0,/r{0,var}/)'

我正在处理每个文件有数千条记录的文本文件。每条记录由两行组成:以 > 开头的 header 和后跟一长串字符的行 -AGTCNR。两条线组成一个完整的记录。
这是一个简单文件的样子:

>ACML500-12|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_-2
----TAAGATTTTGACTTCTTCCCCCATCATCAAGAAGAATTGT-------NNNN
>ACRJP458-10|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
--------NNNTCCCTTTAATACTAGGAGCCCCTGACATAGCCTTTCCTAAATAAT-----
>ASILO303-17|Dip|gs-Par|sp-Par vid|subsp-NA|co
-----TAAGATTCTGATTACTCCCCCCCTCTCTAACTCTTCTTCTTCTATAGTAGATG
>ASILO326-17|Dip|gs-Goe|sp-Goe par|subsp-NA|c
TAAGATTTTGATTATTACCCCCTTCATTAACCAGGAACAGGATGA------
>CLT100-09|Lep|gs-Col|sp-Col elg|subsp-NA|co-Buru
AACATTATATTTGGAANNN-------GATCAGGAATAGTCGGAACTTCTCTGAA------
>PMANL2431-12|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_
----ATGCCTATTATAATTGGAGGATTTGGAAAACCTTTAATATT----CCGAAT
>STBOD057-09|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
ATCTAATATTGCACATAGAGGAACCTCNGTATTTTTTCTCTCCATCT------TTAG
>TBBUT582-11|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
-----CCCCCTCATTAACATTACTAAGTTGAAAATGGAGCAGGAACAGGATGA
>TBBUT583-11|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
TAAGATTTTGACTCATTAA--NNAGTNNNNNNNNNNNNNNNAATGGAGCAGGAACAGGATGA
>AFBTB001-09|Col|gs-NA|sp-NA|subsp-NA|co-Ethi|site-NA|lat_N
TAAGCTCCATCC-------------TAGAAAGAGGGG---------GGGTGA
>PMANL2431-12|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_
----ATGCCTATTAGGAAATTGATTAGTACCTTTAATATT----CCGAAT---
>AFBTB003-09|Col|gs-NA|sp-NA|subsp-NA|co-Ethi|site-NA|lat_N
TAAGATTTTGACTTCTGC------CATGAGAAAGA-------------AGGGTGA
>AFBTB002-09|Cole|gs-NA|sp-NA|subsp-NA|co-Ethi|site-NA|lat_N
-------TCTTCTGCTCAT-------GGGGCAGGAACAGGG----------TGA
>ACRJP458-10|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
NNNNNNNNNNNTCCCTTTAATACTAGGAGCCCCTTTCCT----TAAATAAT-----

使用下面的代码,我可以在第二行中搜索包含字符串的每条记录,并提取具有特定最大数量 -N 的记录或 n 字符在行首使用 $start_gaps 变量和行尾使用 $end_gaps 变量,这是在线程 :

中完成的
start_Ns=10
end_Ns=10
awk -v start_N=$start_Ns -v end_N=$end_Ns ' /^>/ {
hdr=[=12=]; next }; match([=12=],/^[-Nn]*/) && RLENGTH<=start_N && 
match([=12=],/[-Nn]*$/) && RLENGTH<=end_N {
 print hdr; print }' infile.aln > without_shortseqs.aln

现在我需要搜索 -Nn 字符出现在区域 "not including" 第二行的开始或结束终端每条记录并过滤掉超过特定最大字符数 -Nn 字符的记录。下面的代码可以做到,但我需要使用一个可以轻松重置的变量:

start_Ns=10
end_Ns=10
awk -v start_N=10 -v end_N=10 ' /^>/ { 
hdr=[=13=]; next }; match([=13=],/^[-Nn]*/) && RLENGTH<=start_N &&
match([=13=],/[-Nn]*$/) && RLENGTH<=end_N && match([=13=],/N{0,11}/) { 
print hdr; print }' infile.aln > without_shortseqs_mids.aln

至于变量,我尝试了以下但失败了:

awk -v start_N=10 -v mid_N=11 -v end_N=10 ' /^>/ { 
hdr=[=14=]; next }; match([=14=],/^[-Nn]*/) && RLENGTH<=start_N &&
match([=14=],/N{0,mid_N}/) && match([=14=],/[-Nn]*$/) && RLENGTH<=end_N { 
print hdr; print }' infile.aln > without_shortseqs_mids.aln

预期结果:

>ASILO303-17|Dip|gs-Par|sp-Par vid|subsp-NA|co
-----TAAGATTCTGATTACTCCCCCCCTCTCTAACTCTTCTTCTTCTATAGTAGATG
>ASILO326-17|Dip|gs-Goe|sp-Goe par|subsp-NA|c
TAAGATTTTGATTATTACCCCCTTCATTAACCAGGAACAGGATGA------
>CLT100-09|Lep|gs-Col|sp-Col elg|subsp-NA|co-Buru
AACATTATATTTGGAANNN-------GATCAGGAATAGTCGGAACTTCTCTGAA------
>PMANL2431-12|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_
----ATGCCTATTATAATTGGAGGATTTGGAAAACCTTTAATATT----CCGAAT
>STBOD057-09|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
ATCTAATATTGCACATAGAGGAACCTCNGTATTTTTTCTCTCCATCT------TTAG
>TBBUT582-11|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_N
-----CCCCCTCATTAACATTACTAAGTTGAAAATGGAGCAGGAACAGGATGA
>AFBTB001-09|Col|gs-NA|sp-NA|subsp-NA|co-Ethi|site-NA|lat_N
TAAGCTCCATCC-------------TAGAAAGAGGGG---------GGGTGA
>PMANL2431-12|Lep|gs-NA|sp-NA|subsp-NA|co-Buru|site-NA|lat_
----ATGCCTATTAGGAAATTGATTAGTACCTTTAATATT----CCGAAT---
>AFBTB003-09|Col|gs-NA|sp-NA|subsp-NA|co-Ethi|site-NA|lat_N
TAAGATTTTGACTTCTGC------CATGAGAAAGA-------------AGGGTGA

您可以使用字符串作为 match 的第二个参数,然后 Awk 中的常规字符串插值运算符可以正常工作。

awk -v start_N=10 -v mid_N=11 -v end_N=10 ' /^>/ {
        hdr=[=10=]; next }
    match([=10=],/^[-Nn]*/) && RLENGTH<=start_N &&
        match([=10=],"N{0," mid_N "}") &&
            match([=10=],/[-Nn]*$/) && RLENGTH<=end_N {
        print hdr; print }'

稍微说明一下,如果您使用 /regex/,那么斜杠之间的文本会立即被解释为正则表达式,但是如果您使用 "regex" 或一段代码求值为字符串,则首先处理常规 Awk 字符串处理函数,然后才将结果字符串解释为正则表达式。

为了不使事情过于复杂,我建议采用以下逻辑。

  1. 搜索开头部分,将其从字符串中删除
  2. 搜索结束部分,将其从字符串中删除
  3. 在余数中搜索中间部分:
awk -v start_N=10 -v mid_N=11 -v end_N=10 '
   /^>/{hdr=[=10=]; next}
   { seq=[=10=] }
   match(seq,/^[-Nn]*/) && RLENGTH > start_N { next }
   { seq=substr(seq,RSTART+RLENGTH) }
   match(seq,/[-Nn]*$/) && RLENGTH > end_N { next }
   { seq=substr(seq,1,RSTART-1) }
   { while (match(seq,/[-Nn]+/)) { 
        if(RLENGTH>mid_N) next
        seq=substr(seq,RSTART+RLENGTH)
     }
   }
   { print hdr; print [=10=] }' file

另一种方法是使用具有字符重复的扩展正则表达式:

awk -v start_N=10 -v mid_N=11 -v end_N=10 '
   (FNR==1) { ere_start = "^[-Nn]{" start_N+1 ",}"
              ere_end = "[-Nn]{" mid_N+1 ",}$"
              ere_mid = "[^-Nn][-Nn]{" end_N+1 ",}[^-Nn]"
   /^>/{hdr=[=11=]; next}
   { seq=[=11=] }
   match(seq,ere_start) { next }
   match(seq,ere_end) { next }
   match(seq,ere-mid) { next }
   { print hdr; print [=11=] }' file

感谢您的提问。以我的拙见,你应该稍微改一下你的问题,并确保你的 objective 对这个线程的所有潜在读者来说都是 100% 清楚的。

关于在 awk 不允许使用变量的构造中使用变量,有一个标准技巧可以应用您将使用的任何脚本工具(例如 sed 或什至一些更复杂的东西)在 perl 或 Python 中):通过打破单引号结构来中断你的 awk 脚本,然后你在其中插入一个由 shell、不是 由 awk。例如,在这里,您可以在 Bash 中定义 mid_N,然后在您的 awk 脚本中间使用 "${mid_N}",紧接在前面的结束单引号和一个(重新)开始的单引号之后立即报价。像这样:

mid_N=11
awk -v start_N=10 -v end_N=10 ' /^>/ { 
hdr=[=10=]; next }; match([=10=],/^[-Nn]*/) && RLENGTH<=start_N &&
match([=10=],/N{0,'"${mid_N}"'}/) && match([=10=],/[-Nn]*$/) && RLENGTH<=end_N { 
print hdr; print }' infile.aln > without_shortseqs_mids.aln

这是针对您在 "As for a variable i tried the following but failed:"

下方提到的特定问题的最小编辑解决方案