我有一个数据文件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