linux命令:从制表符delim中提取行。第一列包含特定值的文件

时间:2015-01-05 07:30:27

标签: shell cut

我有一个制表符分隔的txt文件。

exon_id "ENSE00002234944"    exon_number "1"     gene_biotype "pseudogene"   gene_id   "ENSG00000223972"     gene_name "DDX11L1"
gene_biotype "pseudogene"    gene_id "ENSG00000223972"   gene_name "DDX11L1"     gene_source "ensembl_havana"    transcript_id "ENST00000456328"
exon_id "ENSE00002234632"    exon_number "1"     gene_biotype "pseudogene"   gene_id "ENSG00000223972"   gene_name "DDX11L1"
gene_biotype "pseudogene"    gene_id "ENSG00000223972"   gene_name "DDX11L1"     gene_source "ensembl_havana"    transcript_id "ENST00000515242"
exon_id "ENSE00002269724"    exon_number "1"     gene_biotype "pseudogene"   gene_id "ENSG00000223972"   gene_name "DDX11L1"
gene_biotype "pseudogene"    gene_id "ENSG00000223972"   gene_name "DDX11L1"     
gene_biotype "pseudogene"        gene_id "ENSG00000269732"       gene_name "WBP1LP7"         gene_source "havana"    transcript_id "ENST00000437905"
exon_id "ENSE00001687828"        exon_number "1"         gene_biotype "lincRNA"  gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"
gene_biotype "lincRNA"   gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"        gene_source "ensembl_havana"    transcript_id "ENST00000440163"
exon_id "ENSE00001628100"        exon_number "2"         gene_biotype "lincRNA"  gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"
exon_id "ENSE00001770724"        exon_number "3"         gene_biotype "lincRNA"  gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"
exon_id "ENSE00001622961"        exon_number "2"         gene_biotype "lincRNA"  gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"
exon_id "ENSE00002202695"        exon_number "2"         gene_biotype "pseudogene"          gene_id "ENSG00000256186"       gene_name "AL732372.1"
gene_biotype "pseudogene"        gene_id "ENSG00000256186"       gene_name "AL732372.1"    gene_source "ensembl"   transcript_id "ENST00000540477"
exon_id "ENSE00002305101"        exon_number "1"         gene_biotype "pseudogene"         gene_id "ENSG00000256186"       gene_name "AL732372.1"
exon_id "ENSE00001651491"        exon_number "1"         gene_biotype "lincRNA"  gene_id "ENSG00000237094"       gene_name "RP4-669L17.10"

正如您所看到的,每一行都有' gene_id'和' gene_name'我试图提取,但列没有对齐。所以我无法使用" cut"这样做。

实际上,我可以使用EXCEL来填充空白字段以对齐和提取我想要的列,但我认为将其用于未来的用法是很好的。

提前谢谢!

2 个答案:

答案 0 :(得分:1)

这就是awk的制作方式:

awk '$1 == "gene_biotype" {print $4, $6}' < input.txt

说明:$ N表示一个字段,默认情况下用空格分隔。任何空白。等式检查说“仅当第一个字段与gene_biotype匹配时才执行剩余的行”。然后打印相应的字段。如果你想删除引用,你可以查看gsub函数,或者你可能超级懒惰并将输出传递给sed

答案 1 :(得分:0)

也许更好的问题是,“我如何正确地规范化这些数据”。您的标记字段格式不适合TSV(它应该是制表符分隔的;键是列索引)。您应该将其转换为符合您要使用的工具要求的格式。

当一组键是静态的,值是简单的,非结构化的文本或数字时,逗号分隔或制表符分隔的文件是有意义的,并且大多数时间都填充了大多数值。

awk -F '\t' 'BEGIN { OFS=FS
 f="exon_id:exon_number:gene_biotype:gene_id:gene_name:gene_source:transcript_id"
   n=split(f, field, /:/); for (i=1; i<=n; ++i) key[field[i]]=i }
 { for (i=1; i<=NF; ++i) {
        split($i, v, / /); gsub(/^"|"$/, "", v[2]); value[key[v[1]]]=v[2] }
    s=""; for (i=1; i<=n; ++i) { printf("%s%s", s, value[i]); s="\t" };
      printf "\n"; delete value }' data.txt >data.new

这会将其标准化,以便第一个字段始终包含exon_idgene_id始终位于第四列,等等(第二行的f值定义字段顺序)。字段名称不再存在于数据中,因为数据在文件中的位置已隐含它们。现在,提取您想要的数据应该是微不足道的。

awk -F '\t' '$3 == "pseudogene" { print $4, $5 }' data.new

有时,CSV / TSV在文件的第一行有列标题,但这是自动处理的麻烦。您应该在一个地方一次性将字段记录到列索引映射。

或者,如果数据的填充程度比您的示例建议的要少,或者您希望自由添加或删除某些字段,和/或某些值具有内部结构,则可能是结构化格式是更合适的。您的示例很容易转换为JSON

awk '{ printf (NR==1 ? "[" : ",\n");
    printf "{"; s="";
    for (i=1; i<NF; i +=2) { printf ("%s\"%s\": %s", s, $i, $(i+1)); s=", " }
    printf "}"; }
  END { printf "]\n" }' data.txt >data.json

有一些替代方案,例如YAML和XML,但JSON简单,灵活,并且支持得很好(而且这里的XML看起来很严重)。现在,您可以按名称引用属性,而不是列号:

 jq '.[] | select(.gene_biotype == "pseudogene") |
    { gene_id, gene_name }' data.json

jq工具特别适用于JSON格式(stream of JSON片段),因此如果您提交JSON工具链而不是通用JSON工具链,则可以进一步简化Awk脚本。这在一定程度上限制了您对工具的选择,但是如果您的需求很简单,那也许没关系(无论如何,相同结构的一系列JSON片段可以很容易地用[..., ..., ...]包装成适当的JSON格式。)

awk '{ printf "{"; s="";
    for (i=1; i<NF; i +=2) { printf ("%s\"%s\": %s", s, $i, $(i+1)); s=", " }
    printf "}"; }' data.txt >data.jsons

然后你可以用

提取
 jq 'select(.gene_biotype == "pseudogene") |
    { gene_id, gene_name }' data.jsons

如果您想要另一个字段而不是“gene_biotype”且值为“pseudogene”,请更新您的问题以指明您要在何种条件下提取值;或者无条件地提取,只需删除select(...)条件或Awk代码中的$3 == "..."