仅当当前行满足给定条件时才保留当前行和上一行

Keep current and previous line only if current line fulfills a given condition

我有一个如下所示的文件:

>4RYF_1
MAENTKNENITNILTQKLIDTRTVLIYGEINQELAEDVSKQLLLLESISNDPITIFINSQGGHVEAGDTIHDMIKFIKPTVKVVGTGWVASAGITIYLAAEKENRFSLPNTRYMIHQPAGGVQGQSTEIEIEAKEIIRMRERINRLIAEATGQSYEQISKDTDRNFWLSVNEAKDYGIVNEIIENRDGLKMASWSHPQFEK
>4RYF_2
MNLIPTVIEQTSRGERAYDIYSRLLKDRIIMLGSAIDDNVANSIVSQLLFLDAQDPEKDIFLYINSPGGSISAGMAIYDTMNFVKADVQTIGMGMAASMGSFLLTAGANGKRFALPNAEIMIHQPLGGAQGQATEIEIAARHILKIKERMNTIMAEKTGQPYEVIARDTDRDNFMTAQEAKDYGLIDDIIINKSGLKGHHHHHH

仅当序列具有给定长度时,我才想保留序列和上一行。为了仅选择符合该条件的行,我使用:

awk 'length([=11=]) > 50 && length([=11=]) <=800)' sample.txt

但是,如果满足此条件,我怎样才能保留以 > 开头的行呢?

AWK 的潜在解决方案是:

awk '!/^>/ {next}; {getline s}; length(s) > 50 && length(s) <= 800 { print [=10=] "\n" s }' example.fasta

例如如果 example.fasta 包含

>4RYF_1
WLSVNEAKDYGIVNEIIENRDGLKMASWSHPQFEK
>4RYF_2
MNLIPTVIEQTSRGERAYDIYSRLLKDRIIMLGSAIDDNVANSIVSQLLFLDAQDPEKDIFLYINSPGGSISAGMAIYDTMNFVKADVQTIGMGMAASMGSFLLTAGANGKRFALPNAEIMIHQPLGGAQGQATEIEIAARHILKIKERMNTIMAEKTGQPYEVIARDTDRDNFMTAQEAKDYGLIDDIIINKSGLKGHHHHHH
>1000_chars
YiJOgeCApTkcJWxIuvooOxuqVnPdSLtOQmUfnzpBvcpYKyCvelFwKgMchYFnlvuZwVxNcnSvGcACsMywDQVvYBAiaIesQkLkYNsExRbqKPZIPnCRMAFHLmIzxIBqLwoNEPSKMZCTpwbbQCNrHSrbDMtCksTjvQsMeAkoudRGUJnPpQTEzwwnKoZBHtpMSIQBfYSPDYHwKktvCiFpewrsdDTQpqBajOWZkKURaKszEqDmdYMkzSAkMtlkXPfHroiTbyxZwzvrrMSXMRSavrBdgVYZanudjacRHWfpErJMkomXpzagXIzwbaeFgAgFnMxLuQHsdvZysqAsngkCZILvVLaFpkWnOpuYensROwkhwqUdngvlTsXBoCBwJUENUFgVdnSnxVOvfksyiabglFPqmSwhGabjNZiWGyvktzSDOQNGlEvoxhJCAOhxVAtZfyimzsziakpzfIszSWYVgKZTHatWSfttHYTkvgafcsVmitfEfQDuyyDAAAoTKpuhLrnHVFKgmEsSgygqcNLQYkpnhOosKiZJKpDolXcxAKHABtALqVXoVcSHpskrpWPrkkZLTpUXkENhnesmoQjonLWxkpcuJrOosXKNTDNuZaWIEtrDILXsIFTjAnrnwJBoirgNHcDURwDIzAXJSLPLmWkurOhWSLPrIOyqNvADBdIFaCGoZeewKleBHUGmKFWFcGgZIGUdOHwwINZqcOClPAjYaLNdLgDsUNCPwKMrOXJEyPvMRLaTJGgxzeoLCggJYTVjlJpyMsoCRZBDrBDckNMhJSQWBAxYBlqSpXnpmLeEJYirwjfCqZGBZdgkHzWGoAMxgNKHOAvGXsIbbuBjeeORhZaIrruBwDfzgTICuwWCAhCPqMqkHrxkQMZbXUIavknNhuIycoDssXlOtbSWsxVXQhWMyDQZWDlEtewXWKBPUcHDYWWgyOerbnoAxrnpsCulOxqxdywFJFoeWNpVGIPMUJSWwvlVDWNkjIBMlXPi

只会打印

>4RYF_2
MNLIPTVIEQTSRGERAYDIYSRLLKDRIIMLGSAIDDNVANSIVSQLLFLDAQDPEKDIFLYINSPGGSISAGMAIYDTMNFVKADVQTIGMGMAASMGSFLLTAGANGKRFALPNAEIMIHQPLGGAQGQATEIEIAARHILKIKERMNTIMAEKTGQPYEVIARDTDRDNFMTAQEAKDYGLIDDIIINKSGLKGHHHHHH

编辑

我建议更好地处理边缘情况的方法是使用专门构建的生物信息学软件,例如seqkit

seqkit seq -m 50 -M 800 example.fasta
>4RYF_2
MNLIPTVIEQTSRGERAYDIYSRLLKDRIIMLGSAIDDNVANSIVSQLLFLDAQDPEKDI
FLYINSPGGSISAGMAIYDTMNFVKADVQTIGMGMAASMGSFLLTAGANGKRFALPNAEI
MIHQPLGGAQGQATEIEIAARHILKIKERMNTIMAEKTGQPYEVIARDTDRDNFMTAQEA
KDYGLIDDIIINKSGLKGHHHHHH

这是一条线:

LANG=C grep -B1 '^.\{51,800\}$' < sample.txt

我默认设置的 LANG=en_US.UTF-8 的命令真的很慢,所以改用 LANG=C。

man grep 告诉您“-B NUM”表示“在匹配行之前打印前导上下文的 NUM 行”。

'^'表示行首
'.'表示任意字符
'{51,800}' 表示我们想要前一个东西的 51 到 800
'$' 表示行尾。

或者换句话说,我们要匹配 51 到 800 个字符之间的行,并打印它和上一行。

请您尝试以下操作:

awk -v RS='>' -F'\n' '
    length() > 50 && length() <= 800 {printf ">%s", [=10=]}
' sample.txt
  • RS 分配给 '>' 告诉 awk> 上的文件拆分为记录, 在同一记录中处理 header 行和序列行。

  • FS 分配给 '\n' 将记录拆分到 header 和 序列,每个分配 </code> 到 header 和 <code> 到序列。

  • 由于前导 > 作为定界符被砍掉了,我们需要在前面加上它 打印匹配记录时。

perl 是一个选项吗?

perl -nle '$prev && print if length() >50 and length() < 800 && print $prev; $prev = $_' input_file

$prev - 创建一个变量来保存每一行。当满足长度条件,并且有前一行$prev,则打印$prev中匹配的条件,并打印最后一行

$prev = $_ 将当前行分配给 prev 行变量


如果上限 800 不是必需的,sed 可以作为一个选项吗?

$ sed -En '/>/ {N;/[a-zA-Z0-9]{50,}/p}' input_file

/>/ - 匹配 > 并读入模式 space

N; 运行 匹配后下一行的条件并将其附加到模式 space also:

{50,} - 如果长度为 50 或更多

/p - Return 它并打印


输出

>4RYF_2
MNLIPTVIEQTSRGERAYDIYSRLLKDRIIMLGSAIDDNVANSIVSQLLFLDAQDPEKDIFLYINSPGGSISAGMAIYDTMNFVKADVQTIGMGMAASMGSFLLTAGANGKRFALPNAEIMIHQPLGGAQGQATEIEIAARHILKIKERMNTIMAEKTGQPYEVIARDTDRDNFMTAQEAKDYGLIDDIIINKSGLKGHHHHHH

另一个awk解决方案:

awk '/^>/ { header = [=10=]; next } length > 50 && length <= 800 { print header ORS [=10=] }'

如果只有下一行满足长度限制,可以将以>开头的行匹配存储在一个变量中,例如previous

然后对于下一行,检查长度以及上一行是否为空。

如果不是,打印上一行和当前行。

最后,将前一个变量设置为空字符串。

awk '{
  if (/^>/) {
    previous = [=10=]
    next
  }
  if (length(previous) != 0 && length([=10=]) > 50 && length([=10=]) <= 800) {
    print previous ORS [=10=]
  }
  previous=""
}' sample.txt

看到 AWK demo

使用您显示的示例,请尝试以下 awk 代码。使用 GNU awk.

编写和测试
awk -v RS= '
{
  val=""
  delete arr
  while(match([=10=],/>[^\n]*\n*[^\n]*/)){
    val=substr([=10=],RSTART,RLENGTH)
    split(val,arr,"\n")
    if(length(arr[2])>50 && length(arr[2])<=800){
      print val
    }
    [=10=]=substr([=10=],RSTART+RLENGTH)
  }
}
'  Input_file