好的,我在SO上找到了类似的答案但是我的sed / grep / awk fu非常糟糕,以至于我无法完全适应我的任务。这是给定的文件" test.gff":
accn|CP014704 RefSeq CDS 403 915 . + 0 ID=AZ909_00020;locus_tag=AZ909_00020;product=transcriptional regulator
accn|CP014704 RefSeq CDS 928 2334 . + 0 ID=AZ909_00025;locus_tag=AZ909_00025;product=FAD/NAD(P)-binding oxidoreductase
accn|CP014704 RefSeq CDS 31437 32681 . + 0 ID=AZ909_00145;locus_tag=AZ909_00145;product=gamma-glutamyl-phosphate reductase;gene=proA
accn|CP014704 RefSeq CDS 2355 2585 . + 0 ID=AZ909_00030;locus_tag=AZ909_00030;product=hypothetical protein
我想提取两个值1)文本右边的#34; ID ="直到分号和2)文本右边的" product ="直到行的结尾或分号(因为你可以看到其中一行也有一个"基因="值。
所以我想要这样的事情:
ID product
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
据我所知:
printf "ID\tproduct\n"
sed -nr 's/^.*ID=(.*);.*product=(.*);/\1\t\2\p/' test.gff
谢谢!
答案 0 :(得分:5)
尝试以下方法:
sed 's/.*ID=\([^;]*\);.*product=\([^;]*\).*/\1\t\2/' test.gff
与您的尝试相比,我改变了您对产品的匹配方式。由于我们不知道字段是以;
还是EOL
结尾,因此我们只匹配尽可能多的非;
字符。我还在末尾添加了.*
以匹配产品后剩余的任何剩余字符。这样,当我们进行替换时,整行将匹配,我们将能够完全重写它。
如果你想要更强大的东西,这里有一个perl one-liner:
perl -nle '($id)=/ID=([^;]*)/; ($prod)=/product=([^;]*)/; print "$id\t$prod"' test.gff
这使用正则表达式分别提取两个字段。它将正常工作,即使字段以相反的顺序显示。
答案 1 :(得分:1)
如果你有GNU-awk又名gawk
,你可以试试下面的内容:
使用awk
gawk 'BEGIN{printf "ID\tProduct%s",RS}
{printf "%s\t%s%s",gensub(/^.*[[:blank:]]+ID=([^;]*);.*$/,"\\1","1",$0),
gensub(/^.*;product=([^;]*)[;]*.*$/,"\\1","1",$0),RS}
' test.gff | expand -t20
<强>输出强>
ID Product
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
AZ909_00030 hypothetical protein
正如你已经注意到的那样,这两个gensub
正在进行繁重的工作。
gensub(/^.*[[:blank:]]+ID=([^;]*);.*$/,"\\1","1",$0)
中,除ID=
与后面的第一个分号之间包含的内容之外的所有内容都会从记录中删除(请参阅$0
)。注意gensub
不修改记录本身,但它只返回打印的修改后的字符串。gensub(/^.*;product=([^;]*)[;]*.*$/,"\\1","1",$0)
中,除product=
与第一个分号(或结尾)之间的内容之外的任何内容都被删除expand -t
来增加标签宽度,以获得格式良好的输出。\n
是一种不好的做法,因此我使用内置记录分隔符变量RS
在每条记录后打印换行符。使用类似逻辑的sed解决方案如下:
使用sed
printf "%-20s%s\n" "ID" "Product"
sed -E "s/^.*[[:blank:]]+ID=([^;]*);.*;product=([^;]*)[;]*.*$/\\1\t\\2/" 39322581 | expand -t20
<强>输出强>
ID Product
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
AZ909_00030 hypothetical protein
考虑到您已经获得了一个简短而优雅的perl
解决方案,如果您有自己的支持,也可以考虑使用它。
附注:在printf中使用\n
会降低脚本的可移植性
答案 2 :(得分:1)
正则表达式的主要问题是使用.*
而不是[^;]*
,因为.*
将匹配所有字符,但您只想匹配非分号。试试这个:
$ sed -E 's/.*ID=([^;]+).*product=([^;]+).*/\1\t\2/' file
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
AZ909_00030 hypothetical protein
或:
$ awk -F'[=;]' -v OFS='\t' '{print $2, $6}' file
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
AZ909_00030 hypothetical protein
您也可以使用awk轻松提取标题值:
$ awk -F'[=;]' -v OFS='\t' 'NR==1{sub(/.* /,"",$1); print $1, $5} {print $2, $6}' file
ID product
AZ909_00020 transcriptional regulator
AZ909_00025 FAD/NAD(P)-binding oxidoreductase
AZ909_00145 gamma-glutamyl-phosphate reductase
AZ909_00030 hypothetical protein
答案 3 :(得分:0)
awk中的另一个人。我们添加&#34;;&#34;到字段分隔符(FS)列表,去掉字符串&#34; ID =&#34;和&#34;产品=&#34;并打印字段9和10:
$ awk -F'([ \t\n]+|;)' 'BEGIN{print "ID" OFS "Product"}{gsub(/product=|ID=/,""); print $9,$10}' test.gff
ID Product
AZ909_00020 locus_tag=AZ909_00020
AZ909_00025 locus_tag=AZ909_00025
AZ909_00145 locus_tag=AZ909_00145
AZ909_00030 locus_tag=AZ909_00030