我有一个如下所示的文件:
chr1 HAVANA exon 12613 12721 . + . gene_id "ENSG00000223972.5"; transcript_id "ENST00000456328.2"; gene_type "transcribed_unprocessed_pseudogene"; gene_name "DDX11L1"; transcript_type "processed_transcript"; transcript_name "DDX11L1-202"; exon_number 2; exon_id "ENSE00003582793.1"; level 2; transcript_support_level "1"; tag "basic"; havana_gene "OTTHUMG00000000961.2"; havana_transcript "OTTHUMT00000362751.1";
chr1 HAVANA exon 13221 14409 . + . gene_id "ENSG00000223972.5"; transcript_id "ENST00000456328.2"; gene_type "transcribed_unprocessed_pseudogene"; gene_name "DDX11L1"; transcript_type "processed_transcript"; transcript_name "DDX11L1-202"; exon_number 3; exon_id "ENSE00002312635.1"; level 2; transcript_support_level "1"; tag "basic"; havana_gene "OTTHUMG00000000961.2"; havana_transcript "OTTHUMT00000362751.1";
我想提取gene_id 和gene_name 值以及前8 列(文件以制表符分隔)。我已经用 perl 编写了一个脚本,可以做到这一点,但我正在 awk、sed 等中寻找一个可以做到这一点的单行程序。
附言。该文件以制表符分隔,有 9 列。第 9 列的值由空格分隔。
我的输出应该是这样的:
chr1 HAVANA exon 12613 12721 . + . ENSG00000223972.5 DDX11L1
chr1 HAVANA exon 13221 14409 . + . ENSG00000223972.5 DDX11L1
答案1
以下awk
脚本假设第 9 列可以包含任意顺序的数据。
该代码将拆分该列,;
后跟一个可选空格。然后它将迭代结果元素并将这些元素按空格分割成键值对。如果键(空格左侧的东西)是两个字符串gene_id
或中的任何一个gene_name
,则会记住该键的值。当我们找到两个字符串时,第 9 列的解析结束,之后该列被重写并打印修改后的行。
该代码还会丢弃任何输入不是包含gene_id
和gene_name
。
BEGIN {
FS = OFS = "\t"
}
{
n = split($9, a, "; ?")
found = 0;
for (i = 1; i <= n; ++i)
if (split(a[i], b, " ") == 2) {
if (b[1] == "gene_id") {
gene_id = b[2]
++found
} else if (b[1] == "gene_name") {
gene_name = b[2]
++found
}
if (found == 2) break
}
if (found == 2) {
$9 = gene_id " " gene_name
print
}
}
对提供的数据进行测试:
$ awk -f script.awk <file
chr1 HAVANA exon 12613 12721 . + . "ENSG00000223972.5" "DDX11L1"
chr1 HAVANA exon 13221 14409 . + . "ENSG00000223972.5" "DDX11L1"
要从值中删除双引号,请更改
if (found == 2) {
$9 = gene_id " " gene_name
print
}
进入
if (found == 2) {
gsub("\"", "", gene_id)
gsub("\"", "", gene_name)
$9 = gene_id " " gene_name
print
}
这会删除基因名称和 ID 中的所有双引号,或者,
if (found == 2) {
gene_id = substr(gene_id, 2, length(gene_id) - 2)
gene_name = substr(gene_name, 2, length(gene_name) - 2)
$9 = gene_id " " gene_name
print
}
它从两个值中删除第一个和最后一个字符。
答案2
Perl 一行代码。它可以打得短一点,但我认为这很清楚。
perl -F'\t' -lane '
if (($id, $name) = / \b gene_id \s+ " ([^"]+) .+ \b gene_name \s+ " ([^"]+)/x) {
print join "\t", @F[0..7], $id, $name;
}
' file
更“聪明”一点:
perl -F'\t' -E '$,="\t"; say @F[0..7], $g{id}, $g{name} if %g = /\bgene_(id|name)\s+"([^"]+)/g' file
答案3
awk '{ print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t" $10 "\t" $16 ; } ' filename > output
不带引号和分号:
awk '{ print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t" $10 "\t" $16 ; }' filename | sed -e 's/;//g; s/\"//g;' > output
仅使用 awk 更准确:
awk '{ ORS=" "; print $1 "\t" $2 "\t" $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7 "\t" $8 "\t"; gsub(";", "", $10); gsub("\"", "", $10); print $10 "\t"; gsub(";", "", $16) ; gsub("\"", "", $16); print $16 ; ORS="\n" ; print " "; } ' filename > output