如何用Bash脚本分割大CSV文件,确保同日期数据不被拆分到多文件
实现思路
- 先对所有CSV内容做预处理,统计每个日期对应的总行数,同时输出所有去重后的日期列表
- 采用贪心分配规则:依次将每个日期分配到当前总行数最小的分组,既保证同一日期所有行归属同一个文件,也能让各分割文件的行数尽可能接近
- 最后遍历所有数据行,按日期对应的分组编号写入对应文件,所有输出文件会先统一写入表头
完整可运行脚本
#!/bin/bash VERSION=$1 # 分割份数默认取CPU核心数,也可通过第二个参数手动指定 NPROC=${2:-$(nproc)} INPUT_FILES="${VERSION}_*.csv" # 第一步:给所有输出文件写入表头 head -n1 $(ls $INPUT_FILES | head -n1) | awk -v n="$NPROC" '{ for(i=0;i<n;i++) print $0 > "part_"i".csv" }' # 第二步:统计每个日期的行数,同时生成日期分配规则 awk -F';' ' FNR==1 {next} # 跳过所有文件的表头 { cnt[$1]++ # 第一列是DATE,统计每个日期的行数 } END { nproc = '"$NPROC"' # 初始化每个分组的当前总负载 for(i=0;i<nproc;i++) group_load[i] = 0 # 遍历所有日期,贪心分配到负载最小的分组 for(date in cnt) { min_load = group_load[0] min_idx = 0 for(i=1;i<nproc;i++) { if(group_load[i] < min_load) { min_load = group_load[i] min_idx = i } } date_group[date] = min_idx group_load[min_idx] += cnt[date] } # 输出日期到分组的映射规则 for(date in date_group) print date "\t" date_group[date] } ' $INPUT_FILES > date_group_map.tmp # 第三步:根据映射规则,把所有数据行写入对应分组文件 awk -F';' ' FNR==1 {next} # 跳过表头 NR==FNR { # 先加载日期分组映射 date_group[$1] = $2 next } { # 按日期找对应分组,写入文件 outfile = "part_" date_group[$1] ".csv" print $0 >> outfile } ' date_group_map.tmp $INPUT_FILES # 清理临时文件 rm date_group_map.tmp
使用说明
- 执行方式:
./split.sh [版本号] [可选:分割份数],比如./split.sh v1默认按CPU核心数分割,./split.sh v2 12指定分割为12份 - 脚本默认CSV字段分隔符为分号
;,如果是逗号分隔的CSV,把两处-F';'修改为-F','即可 - 分割后的文件命名为
part_0.csv到part_{N-1}.csv,都自带表头,同一日期的所有行都会在同一个文件中
内容的提问来源于stack exchange,提问作者Guido
相关产品推荐
相关产品推荐

