如何用Bash的cut/awk单次遍历拆分2TB超大VCF文件为19个单列文件?
一次遍历拆分超大VCF文件为19个单列文件的Bash方案
刚好之前处理过类似的超大基因组文件拆分需求,完全理解你担心多次IO的痛点——100个2TB的文件,要是每个都遍历19次,那IO开销简直灾难。用awk就能完美实现单遍历拆分,下面直接上可落地的方案:
核心思路
awk可以一次读取每一行,先按制表符拆分出原8列,再把第8列按分号拆成12个子字段,最后把所有19个字段分别写入对应的单列文件,全程只遍历输入文件一次,把IO成本降到最低。
单个文件处理脚本
awk -F'\t' '{ # 把第8列按分号拆分为数组 split($8, subfields, /;/) # 处理前7个原始列,写入对应文件 print $1 > "col1.txt" print $2 > "col2.txt" print $3 > "col3.txt" print $4 > "col4.txt" print $5 > "col5.txt" print $6 > "col6.txt" print $7 > "col7.txt" # 处理第8列拆分后的12个子列(对应总第8到19列) for (i=1; i<=12; i++) { # 处理子字段缺失的情况,确保每列行数一致 print (subfields[i] ? subfields[i] : "") > "col" (7+i) ".txt" } # 关键:关闭文件句柄,避免达到系统打开文件上限 for (i=1; i<=19; i++) { close("col" i ".txt") } }' your_large_file.vcf
关键细节说明
- 分隔符设置:
-F'\t'指定制表符为原始列的分隔符,精准拆分原8列 - 子字段拆分:
split($8, subfields, /;/)把第8列的分号分隔内容拆成数组,方便逐个提取 - 空值处理:
subfields[i] ? subfields[i] : ""保证如果某个子字段缺失(比如第8列分号数量不足11个),会输出空字符串,避免单列文件行数不一致 - 文件句柄管理:
close()是必须的——awk默认不会自动关闭打开的文件,处理百万级行时很容易触发系统打开文件数上限,循环关闭能避免这个问题
批量处理100个文件的脚本
如果要批量处理所有VCF文件,建议给每个文件创建独立目录存储拆分结果,避免文件名冲突:
# 遍历当前目录下所有.vcf文件 for vcf_file in *.vcf; do # 创建对应拆分目录(去掉.vcf后缀) split_dir="${vcf_file%.vcf}_split" mkdir -p "$split_dir" awk -F'\t' -v output_dir="$split_dir" '{ split($8, subfields, /;/) # 处理前7列 for (i=1; i<=7; i++) { print $i > output_dir "/col" i ".txt" } # 处理拆分后的12列 for (i=1; i<=12; i++) { print (subfields[i] ? subfields[i] : "") > output_dir "/col" (7+i) ".txt" } # 关闭所有文件句柄 for (i=1; i<=19; i++) { close(output_dir "/col" i ".txt") } }' "$vcf_file" done
额外注意事项
- 磁盘空间:拆分后的19个文件总大小和原文件基本一致,确保目标磁盘有足够空间(每个2TB文件需要至少2TB可用空间)
- 系统限制:如果遇到"too many open files"错误,可以临时提高系统打开文件数上限,比如
ulimit -n 1024(19个文件完全够用) - 性能:awk的处理效率非常高,比多次调用
cut快得多,因为只做一次文件读取操作
内容的提问来源于stack exchange,提问作者Howard
相关产品推荐
相关产品推荐

