【问题标题】:Select specific fields and write them to a new file选择特定字段并将它们写入新文件
【发布时间】:2021-12-21 04:51:52
【问题描述】:

我的任务是将一些文件汇总到一个 tsv 文件中。我必须从文件列表中选择特定数据并将其写入 tsv 文件中的一行制表符分隔的列。文件中的每一行都有一个“名称”作为第一列,因此很容易过滤数据($1 == "NAME")。一个文件 == tsv 中的一行。到目前为止,我写了这个:

#! /bin/bash
cat > newFile.txt
for f in *.pdb; do
awk '$1 == "ACCESSION" {print $2}' ORS="/t" "$f" >> newFile.txt
awk '$1 == "DEFINITION" {print $2}' ORS="/t" "$f" >> newFile.txt
awk '$1 == "SOURCE" {print $2}' ORS="/t" "$f" >> newFile.txt
awk '$1 == "LOCUS" {print$4}' ORS="/r" "$f" >> newFile.txt
done

显然,这种代码的暴行是行不通的。是否可以修改我写的内容并使用 awk 完成任务?

文件示例:

LOCUS \t NM_123456 \t 2000bp \t mRNA
DEFINITION \t Very nice gene from a very nice mouse
ACCESSION \t NM_123456
VERSION \t 1.000
SOURCE \t Very nice mouse

最终结果:

NM_123456 /t Very nice gene from a very nice mouse /t Very nice mouse /t mRNA
NM_345678 /t Not so nice gene from an angry elephant /t Angry Elephant /t mRNA

"/t" 代表一个制表符(我不知道怎么写,抱歉)。示例文件还包含更多信息,我只是给出了一个“标题”。

【问题讨论】:

  • 您能否提供示例文件和预期输出?
  • edit您对示例的问题,不要将其添加到cmets :)
  • 嘿。我已按照您的要求修改了问题。我希望它足够清楚^^
  • 恭喜!你能告诉我们一个文件是否只能有一个同名字段吗? (即,只有一个 ACCESSION / DEFINITION / LOCUS / VERSION / SOURCE)
  • 是的,字段名称是唯一的!

标签: bash shell awk


【解决方案1】:

在普通的 bash 中:

for file in *.pdb; do
    acc=
    def=
    src=
    loc=
    while IFS=$'\t' read -ra fields; do
        if [[ ${fields[0]} = "ACCESSION" ]]; then
            acc=${fields[1]}
        elif [[ ${fields[0]} = "DEFINITION" ]]; then
            def=${fields[1]}
        elif [[ ${fields[0]} = "SOURCE" ]]; then
            src=${fields[1]}
        elif [[ ${fields[0]} = "LOCUS" ]]; then
            loc=${fields[3]}
        fi
    done < "$file"
    printf '%s\t%s\t%s\t%s\n' "$acc" "$def" "$src" "$loc" >> newFile.txt
done

【讨论】:

  • 嗯,我明白你做了什么,谢谢!你能解释一下为什么我不能使用 'awk' 以及为什么我的代码是垃圾?
  • @PetrasNasvytis 当然,您可以使用 awk。但是不推荐在 shell 循环中调用 awk。您可以完全在 awk 中完成这项任务。我是用 bash 做的,因为问题中没有 awk 标签。
  • 啊,我明白了。仍然不完全确定这一切是如何工作的:D 这是我发布的第一个问题。我会记住你的评论:) 谢谢
  • @M.NejatAydin 不建议在 shell 循环中调用 awk 是正确的,但解决方案是摆脱 shell 循环,而不是摆脱 awk。见why-is-using-a-shell-loop-to-process-text-considered-bad-practice
【解决方案2】:

如果每个文件的这些行在每个文件中的顺序相同,并且它们每个文件只出现一次(不多也不少),您可以这样做:

awk '
$1 == "ASCESSION" {printf "%s\t", $2}
$1 == "DEFINITION" {printf "%s\t", $2}
$1 == "SOURCE" {printf "%s\t", $2}
$1 == "LOCUS" {print $4}' *.pdb > table.tsv

但是,如果行的顺序不同,或者某些文件不是每一行,或者某些文件有多行相同(例如SOURCE foo 出现两次),您将需要更复杂的东西,例如:

awk '
function print_row(cols) {
    for (i=0; i<3; i++) {
        printf "%s\t", cols[i]
        cols[i] = ""
    }
    print cols[3]
    cols[3] = ""
}

NR!=FNR && FNR==1 {print_row(cols)}

$1 == "ASCESSION" {cols[0] = $2}
$1 == "DEFINITION" {cols[1] = $2}
$1 == "SOURCE" {cols[2] = $2}
$1 == "LOCUS" {cols[3] = $4}

END {print_row(cols)}' *.pdb > table.tsv

它总是打印一个整洁的表格,列排列正确,无论文件中的行顺序如何,即使某些行丢失或出现多次。如果一行出现多次,则使用最后一次出现。

【讨论】:

    【解决方案3】:

    如果支持ENDFILE块的gawk可用,请尝试:

    awk -F'\t' -v OFS='\t' '                # assign input/output field separator to a tab character
    BEGIN {
        split("ACCESSION,DEFINITION,SOURCE,LOCUS", names, ",")
                                            # assign an array "names" to the list of names
    }
    {
        if ($1 == "LOCUS") a[$1] = $4
        else a[$1] = $2
    }
    ENDFILE {                               # this block is invoked after reading each file
        print a[names[1]], a[names[2]], a[names[3]], a[names[4]]
                                            # print a["ACCESSION"], a["DEFINITION"], .. in order as a tsv
        delete a                            # clear array "a"
    }' *.tsv
    

    【讨论】:

      【解决方案4】:

      这可能是您正在寻找的,在每个 Unix 机器上的任何 shell 中使用任何 awk(未经测试):

      awk '
      BEGIN { FS=OFS="\t" }
      { f[$1] = ($1 == "LOCUS" ? $4 : $2) }
      $1 == "SOURCE" {
          print f["ACCESSION"], f["DEFINITION"], f["SOURCE"], f["LOCUS"]
      }
      ' *.pdb > newFile.txt
      

      以上假设每个输入文件都具有与您问题中的输入文件中所示相同的标签值对,并且 SOURCE 始终是最后一个。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-25
        • 2020-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多