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

linux command : extracting lines from a tab delim. file whose first column containing a certain value

我有一个制表符分隔的 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来填充空字段来对齐和提取我想要的列,但我认为学习它以备将来使用。

提前致谢!

这几乎就是 awk 的用途:

awk ' == "gene_biotype" {print , }' < input.txt

说明:$N表示一个字段,默认以空格分隔。任何空格。相等性检查显示 "Execute the rest of the line only when the first field matches gene_biotype"。然后打印适当的字段。如果你想删除引号,你可以查看 gsub 函数,或者你可以超级懒惰并将输出通过管道传输到 sed.

也许更好的问题是 "how do I properly normalize this data"。您的标记字段格式不适合 TSV(它应该只是 tab-separated values;键是列索引)。您可能应该将其转换为与您要使用的工具的要求一致的格式。

comma-separated 或 tab-separated 文件在键集是静态的、值是简单的、非结构化的文本或数字时有意义,并且大多数值在大多数时间都被填充。

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_id 并且 gene_id 始终在第四列中,依此类推(第二行的 f 值定义字段顺序).字段名称不再存在于数据中,因为它们已经隐含在数据在文件中的位置中。现在,提取您想要的数据应该是微不足道的。

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

有时,CSV/TSV 的列标题位于文件的第一行,但这对自动化处理来说很麻烦。您应该在一个地方一劳永逸地记录字段到列索引的映射。

或者,如果数据比您的示例建议的更稀疏,或者如果您希望不时自由地添加或删除一些字段,and/or如果某些值具有内部结构,也许结构化格式更合适。您的示例很容易转换为 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 简单、灵活,而且 well-supported(XML 在这里似乎有点矫枉过正)。现在,您可以通过名称而不是列号来引用属性:

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

jq tool in particular is fine with JSONs format (a stream of JSON 片段),因此如果您使用 JSONs 工具链而不是一般的 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 代码中的 == "..."