如何 grep 从文件中提取前 n 个字符

如何 grep 从文件中提取前 n 个字符

我正在尝试使用从文件中提取一些数据grep

该文件是一个 DNA fasta 文件,包含以下行:

ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT

我需要找到TA前面的所有 20 个字符TA

我目前正在尝试:grep -E -o ".{0,20}TA"

但这会产生输出,给出字符串之间的字符TA- 在上面的行中给出 - 例如:

TCGATGCTGCTA 
GCATCGTA 

这是两次出现之间的字符串TA,我想得到:

TAGCATCGTATCGATGCTGCTA
TCGTATCGATGCTGCTATGCTA

其中包含搜索字符串的实例。

有没有办法做到这一点grep

答案1

由于您想要重叠的字符串,恐怕默认情况下没有工具可以提供这一点。有必要循环输入以查找所有重叠的事件。下一个问题是正则表达式的贪婪本质:ATCGTA如果ATCGTAGCTA可以找到a,您将找不到前导。这使得循环变得更加复杂:

sed -E ':1
 h;s/(.*TA).*/\1/
 s/.{0,20}TA$/_&/
 s/.*_//p
 g;s/(.*)TA.*/\1/;t1
 d

是我能想到的第一个解决方案。该示例的输出应包含您想要的所有序列:

GATGCTGCTATGCTAGATGCTA
TCGTATCGATGCTGCTATGCTA
TAGCATCGTATCGATGCTGCTA
ATCGTAGCTAGCATCGTA
ATCGTAGCTA
ATCGTA

解释:从上一场比赛开始似乎更容易,所以

  • h将缓冲区保存在保持空间中以供下一个周期使用
  • s/(.*TA).*/\1/删除最后一个之后的所有内容TA
  • s/.{0,20}TA$/_&/在您想要获取的序列的开头放置一个下划线作为标记
  • s/.*_//p删除标记之前的所有内容并打印序列
  • 为下一个周期做准备,g恢复保存的模式并s/(.*)TA.*/\1/删除最后一个TA和后续的,这样就不会再找到它
  • 最后从找到序列的时候t1开始。:1
  • d抑制最后的虚假输出

答案2

给定序列中只有 3 个子序列,其中 20 个碱基后跟TA。这些都是相互重叠的。该grep实用程序不能用于提取重叠的字符串,因为必须多次遍历线路才能找到所有子字符串。

ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT
----TA--TA------TA----------TA---TA-----TA--
                    01234567890123456789
             01234567890123456789
        01234567890123456789

这些序列可以通过以下脚本找到sed(编写为与 一起使用sed -n):

:again
s/\(.*.\{20\}TA\).*/\1/
h
s/.*\(.\{20\}TA\)/\1/p
g
s/TA$//
t again
  1. 第一个命令是标签 ,again我们将使用它来处理输入行中的下一个子序列。
  2. 第一个替换会删除最后一个之后的所有序列TA
  3. h修剪后的序列放入“保留空间”(中的临时缓冲区sed)。
  4. 第二次替换找到最后一个 20 个碱基序列,然后TA将其打印出来。
  5. 从保留空间中检索g先前存储的序列(丢弃刚刚打印的序列)。
  6. 第三次替换TA从字符串末尾删除了 。
  7. 如果最近的替换实际上做了任何事情,则该t命令会跳转到标签。again

测试它:

$ sed -n -f script.sed file
GATGCTGCTATGCTAGATGCTA
TCGTATCGATGCTGCTATGCTA
TAGCATCGTATCGATGCTGCTA

如果将单个sed命令添加=到脚本的最顶部sed,您还将获得有关哪个输入行产生什么输出的指示。下面显示了数据在三行中重复:

$ sed -n -f script.sed file
1
GATGCTGCTATGCTAGATGCTA
TCGTATCGATGCTGCTATGCTA
TAGCATCGTATCGATGCTGCTA
2
GATGCTGCTATGCTAGATGCTA
TCGTATCGATGCTGCTATGCTA
TAGCATCGTATCGATGCTGCTA
3
GATGCTGCTATGCTAGATGCTA
TCGTATCGATGCTGCTATGCTA
TAGCATCGTATCGATGCTGCTA

答案3

也许有一种方法可以获得重叠匹配grep -o(我不知道任何,甚至不知道grep -Po),但同时你可以使用awk

echo ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT |
awk '{
   i=0; for(s=$0; j = index(s,"TA"); s = substr($0, i + 1))
            print ((i += j) > 20) ? substr($0, i - 20, 22) : substr($0, 1, i+1)
}'

ATCGTA
ATCGTAGCTA
ATCGTAGCTAGCATCGTA
TAGCATCGTATCGATGCTGCTA
TCGTATCGATGCTGCTATGCTA
GATGCTGCTATGCTAGATGCTA

如果您不希望从字符串开头开始进行较短的匹配,请将其简化为:

echo ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT |
awk '{
    i=0; for(s=$0; j = index(s,"TA"); s = substr($0, i + 1))
             if((i += j) > 20) print substr($0, i - 20, 22)
}'

TAGCATCGTATCGATGCTGCTA
TCGTATCGATGCTGCTATGCTA
GATGCTGCTATGCTAGATGCTA

同样的事情在perl

echo ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT |
perl -nle 'print $-[0] > 20 ? substr $_, $-[0]-20, 22 : substr $_, 0, $+[0] while /TA/g'

ATCGTA
ATCGTAGCTA
ATCGTAGCTAGCATCGTA
TAGCATCGTATCGATGCTGCTA
TCGTATCGATGCTGCTATGCTA
GATGCTGCTATGCTAGATGCTA


echo ATCGTAGCTAGCATCGTATCGATGCTGCTATGCTAGATGCTAGT |
perl -nle 'pos() -= 21, print $1 while /(.{20}TA)/g'

TAGCATCGTATCGATGCTGCTA
TCGTATCGATGCTGCTATGCTA
GATGCTGCTATGCTAGATGCTA

如果使用mawk(类似 debian 的默认值),该awk版本会更快。

虽然可以很好地展示sed熟练程度,但任何sed解决方案都必然有很多数量级perl或慢awk

相关内容