如何连接多个文件中的特定列

如何连接多个文件中的特定列

我有一个数据文件A.tsv(字段分隔符= \t):

id  clade   mutation
243 40A SDF
254 20B
261 40A
267 20B

B.tsv(字段分隔符 = \t):

id  clade   mutation
243 40A
254 20B
261 40A
267 20B SLT

C.tsv(字段分隔符 = \t):

id  clade   mutation
243 40A
254 20B
261 40A MGG
267 20B

我想将所有文件连接成一个,知道所有文件之间唯一不同的列是 column mutation。预期结果是:

id  clade   mutation
243 40A SDF
254 20B
261 40A MGG
267 20B SLT

到目前为止我尝试过:

awk 'BEGIN{FS=OFS="\t"} NR==FNR{klos[NR]=$3; next} $3==""{$3=klos[FNR]} 1' *.tsv > output.tsv

但它不起作用。你有办法做到吗?谢谢

PS:这是一个示例测试,在我的真实情况下我有超过3个文件。但就像示例一样,mutation列中不存在具有相同内容的两个文件。

答案1

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
(NR == FNR) || ($3 != "") {
    fnr2rec[FNR] = $0
}
FILENAME == ARGV[ARGC-1] {
    print fnr2rec[FNR]
}

$ awk -f tst.awk A.tsv B.tsv C.tsv
id      clade   mutation
243     40A     SDF
254     20B
261     40A     MGG
267     20B     SLT

答案2

然后使用paste以下方法处理其输出awk

paste file[ABC].tsv |\
awk 'BEGIN{ FS=OFS="\t" } { for(i=3; i<=NF; i+=3) if($i"")break; print $1, $2, $i }'

使用的条件if($i"")break是一旦找到第一个非空字段就中断 for 循环(双引号用于强制 awk 进行字符串比较),然后只打印字段 #1、#2 和该非空字段之前在字段 #i 中找到过。

答案3

这应该可行(如果您不介意结果相反)。

[root@SE ~]# awk 'BEGIN{FS=OFS="\t"} { if($3>0) { print }}' A.tsv B.tsv C.tsv | sort -ru

我确信可能有一种更简单的方法来实现这一目标,因为我对awk.

我所做的只是打印所有 3 个文件并删除突变中不带字符的所有行。然后管道sort -u删除所有重复项。该-r标志颠倒了顺序。

[root@SE ~]# awk 'BEGIN{FS=OFS="\t"} { if($3>0) { print }}' A.tsv B.tsv C.tsv | sort -ru
id  clade   mutation
267 20B SLT
261 40A MGG
254 20B SLT
243 40A SDF

@Quasimodo 简化了这一点。谢谢。

awk 'BEGIN{FS=OFS="\t"} (FNR>1 || NR==1) && $3' A.tsv B.tsv C.tsv

如果它是处理的第一行(即第一个输入文件的标题),或者是任何被处理的行,这将打印一行。不是标头,但包含非空的第三个字段。它之所以有效,是因为规则块 ( { ... }) 之外的布尔表达式的计算结果为“true”,指示awk打印当前行。

答案4

使用 bash 命令行实用程序的一种方法是:

## initialization 
ref= tmpdir=$(mktemp -d) cn=3

for f in ./*.tsv;do
  : ${ref:="$f"}
 < "$f" cut -f"$cn" > "$tmpdir/$f"
done

paste <(cut -f-"$((cn-1))" "$ref") "$tmpdir"/* |
tr -s '\t' |
sed -ne "1s/\t/\n/$cn" -e 's/\t*$//;P'

结果:

id  clade   mutation
243 40A SDF
254 20B
261 40A MGG
267 20B SLT

相关内容