You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bash中拆分文件时更新表头的行数与指定列求和值

拆分带统计信息表头的大文件并更新子文件表头

问题背景

我有一个包含数十亿条记录的大文件,文件首行是表头,其中包含元信息、总行数(第5个字段,分隔符为|)以及第6列的求和值。现在需要将文件拆分为指定行数的小文件,每个子文件的表头必须更新为对应子文件的实际记录行数和第6列的求和值。

示例输入文件(testFile.text)

00|STMT|08-09-2022 13:24:56||5|13.10|SHA2
10|000047290|8ddcf4b2356dfa7f326ca8004a9bdb6096330fc4f3b842a971deaf660a395f65|18-01-2020|12:36:57|3.10|00004729018-01-20201|APP
10|000052736|cce280392023b23df2a00ace4b82db8eb61c112bb14509fb273c523550059317|07-02-2017|16:27:49|2.00|00005273607-02-20171|APP
10|000070355|f2e86d2731d32f9ce960a0f5883e9b688c7e57ab9c2ead86057f98426407d87a|17-07-2019|20:25:02|1.00|00007035517-07-20192|APP
10|000070355|54c1fc2667e160a11ae1dbf54d3ba993475cd33d6ececdd555fb5c07e64a241b|17-07-2019|20:25:02|5.00|00007035517-07-20192|APP
10|000072420|f5dac143082631a1693e0fb5429d3a185abcf3c47b091be2f30cd50b5cf4be11|14-06-2021|20:52:21|2.00|00007242014-06-20212|APP

预期拆分结果

testFile_1.text

00|STMT|08-09-2022 13:24:56||3|6.10|SHA2
10|000047290|8ddcf4b2356dfa7f326ca8004a9bdb6096330fc4f3b842a971deaf660a395f65|18-01-2020|12:36:57|3.10|00004729018-01-20201|APP
10|000052736|cce280392023b23df2a00ace4b82db8eb61c112bb14509fb273c523550059317|07-02-2017|16:27:49|2.00|00005273607-02-20171|APP
10|000070355|f2e86d2731d32f9ce960a0f5883e9b688c7e57ab9c2ead86057f98426407d87a|17-07-2019|20:25:02|1.00|00007035517-07-20192|APP

testFile_2.text

00|STMT|08-09-2022 13:24:56||2|7.00|SHA2
10|000070355|54c1fc2667e160a11ae1dbf54d3ba993475cd33d6ececdd555fb5c07e64a241b|17-07-2019|20:25:02|5.00|00007035517-07-20192|APP
10|000072420|f5dac143082631a1693e0fb5429d3a185abcf3c47b091be2f30cd50b5cf4be11|14-06-2021|20:52:21|2.00|00007242014-06-20212|APP

现有脚本问题

目前已实现文件拆分和求和计算,但无法更新子文件表头中的行数和求和值,现有脚本如下:

#!/bin/bash

splitRowCount=$1
transactionColumn=$2

filename=$(basename -- "$3")
extension="${filename##*.}"
nameWithoutExt="${filename%.*}"

echo "splitRowCount: $splitRowCount"
echo "transactionColumn: $transactionColumn"


awk 'NR == 1 { head = $0 } NR % '$splitRowCount' == 2 { filename = "'$nameWithoutExt'_" int((NR-1)/'$splitRowCount')+1 ".'$extension'"; print head > filename } NR != 1 { print >> filename }' $filename


 ls *.txt | while read line
 do
  firstLine=$(head -n 1 $line);
  awk -F '|'  'NR !=1 {sum += '$transactionColumn'}END {print sum} '   $line
 done

解决方案

修改脚本,在拆分过程中直接统计每个子文件的行数和求和值,生成正确的表头,避免事后修改的麻烦(尤其适合数十亿行的大文件,减少IO操作):

#!/bin/bash

splitRowCount=$1
# 第6列的索引是6,这里默认取6,也可通过参数传入
transactionCol=${2:-6}
inputFile=$3

filename=$(basename -- "$inputFile")
extension="${filename##*.}"
nameWithoutExt="${filename%.*}"

echo "splitRowCount: $splitRowCount"
echo "transactionColumn: $transactionCol"

awk -v split_rows="$splitRowCount" -v trans_col="$transactionCol" \
    -v base_name="$nameWithoutExt" -v ext="$extension" '
BEGIN {
    FS = "|"
    OFS = "|"
    file_num = 0
    row_count = 0
    sum_val = 0.0
}
NR == 1 {
    # 保存表头的固定字段,仅动态替换第5(行数)和第6(求和值)字段
    fixed_header[1] = $1
    fixed_header[2] = $2
    fixed_header[3] = $3
    fixed_header[4] = $4
    fixed_header[7] = $7
    next
}
{
    # 每达到指定行数就新建一个子文件
    if (row_count % split_rows == 0) {
        # 处理上一个子文件(如果存在)
        if (file_num > 0) {
            # 生成更新后的表头
            printf "%s%s%s%s%s%s%s%s%d%s%.2f%s%s\n", 
                fixed_header[1], OFS, fixed_header[2], OFS, fixed_header[3], OFS,
                fixed_header[4], OFS, row_count, OFS, sum_val, OFS, fixed_header[7] > out_file
            # 写入缓存的记录行
            for (i=1; i<=row_count; i++) {
                print line_cache[i] > out_file
            }
            close(out_file)
            delete line_cache
        }
        file_num++
        out_file = base_name "_" file_num "." ext
        row_count = 0
        sum_val = 0.0
    }
    row_count++
    sum_val += $trans_col
    line_cache[row_count] = $0
}
END {
    # 处理最后一个子文件
    if (file_num > 0) {
        printf "%s%s%s%s%s%s%s%s%d%s%.2f%s%s\n", 
            fixed_header[1], OFS, fixed_header[2], OFS, fixed_header[3], OFS,
            fixed_header[4], OFS, row_count, OFS, sum_val, OFS, fixed_header[7] > out_file
        for (i=1; i<=row_count; i++) {
            print line_cache[i] > out_file
        }
        close(out_file)
    }
}' "$inputFile"

关键改进点

  • 一次性处理:拆分同时统计行数和求和值,无需事后遍历修改表头,大幅减少IO开销,适配超大型文件
  • 表头复用:仅动态替换表头中需要更新的字段,保留其余元信息不变
  • 精度控制:用%.2f保证求和值保留两位小数,和示例格式一致
  • 缓存机制:缓存子文件的记录行,最后和表头一起写入,避免频繁文件IO操作

使用方式与原脚本一致:

./split_script.sh 3 6 testFile.text

内容的提问来源于stack exchange,提问作者Muddassir Rahman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 07:31:19