如何在Bash中从多个TSV表的列生成新合并表?
解决方法
方法一:用cut+paste(简单易理解)
这种方法依赖基础文本处理命令,步骤清晰,适合快速上手:
- 从第一个TSV文件提取
name列作为新表的首列:
cut -f1 first_sample.tsv > name_col.tmp
将first_sample.tsv替换成你的任意一个目标文件名即可。
- 遍历所有TSV文件,提取每个文件的第5列(
reads列)并保存为临时文件:
for f in *.tsv; do cut -f5 "$f" > "${f%.tsv}_reads.tmp" done
${f%.tsv}会自动去掉文件名的.tsv后缀,生成的临时文件会以xxx_reads.tmp命名。
- 用
paste命令将name列和所有reads临时文件按行合并,生成最终表格:
paste name_col.tmp *_reads.tmp > reads_table.tsv
- 清理临时文件:
rm name_col.tmp *_reads.tmp
方法二:用awk(高效无临时文件)
针对50个文件的场景,awk可以一次性处理所有文件,无需生成临时文件,效率更高:
awk -F'\t' ' # 处理表头行 FNR == 1 { if (NR == FNR) { # 第一个文件:输出表头,首列为name,后续列为去掉.tsv后缀的文件名 printf "name" for (i=1; i<ARGC; i++) { sub(/\.tsv$/, "", ARGV[i]) printf "\t%s", ARGV[i] } print "" } next # 跳过所有文件的表头行 } # 处理第一个文件的内容:存储name列到数组 NR == FNR { name[FNR] = $1 next } # 处理其他文件:存储对应行的reads列(第5列) { reads[ARGIND][FNR] = $5 } # 最终输出所有内容 END { for (line=2; line<=length(name); line++) { printf "%s", name[line] for (file_idx=2; file_idx<ARGC; file_idx++) { printf "\t%s", reads[file_idx][line] } print "" } }' *.tsv > reads_table.tsv
注意事项
- 必须确保所有TSV文件的
name列顺序和内容完全一致,否则两种方法都会出现行不匹配的问题。 - 如果文件名包含空格或特殊字符,务必用双引号包裹文件名变量(比如方法一中的
"$f")。 - 若不需要处理所有
.tsv文件,可替换*.tsv为具体的文件名列表,比如sample1.tsv sample2.tsv ...。
内容的提问来源于stack exchange,提问作者Valentin
相关产品推荐
相关产品推荐

