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

如何用Linux命令行工具合并CSV列,使每行固定为14列?

用awk轻松搞定CSV列合并,固定为14列

嘿,你的需求用awk来处理最顺手了——它能灵活应对每行14到20列的差异,一次性完成列合并和格式调整,比cut+paste的组合方案高效太多。下面分两种常见场景给你具体方案:

场景1:CSV里没有带逗号的单元格(简单情况)

如果你的CSV所有逗号都是列分隔符,没有单元格用引号包裹并包含逗号(比如"Smith, Jane"这种情况),直接用这个简洁的awk脚本就行:

BEGIN { FS = ","; OFS = "," }
{
    # 算清楚需要合并多少次:原列数减14,每次合并减少1列
    merge_times = NF - 14
    for (i=1; i<=merge_times; i++) {
        # 把第10列和第11列合并,后面的列依次往前挪一位
        $10 = $10 "," $11
        for (j=11; j<NF; j++) {
            $j = $(j+1)
        }
        NF--  # 列数减1,确保下次合并的位置正确
    }
    print  # 输出调整后的14列
}

把这段代码存成merge_csv_cols.awk,然后运行:

awk -f merge_csv_cols.awk your_data.csv

简单解释下:

  • 开头的BEGIN块把输入输出的分隔符都设为逗号,保证CSV格式不乱。
  • 对每一行,先算出需要合并的次数:比如原行有20列,那就要合并6次(20-14=6),每次都把当前的第10和11列合并,然后后面的列往前移,直到总列数变成14。
  • 最后直接输出调整好的行就行。

场景2:CSV存在带逗号的引号包裹单元格(复杂情况)

如果你的CSV里有单元格用双引号包着,而且里面还有逗号(比如用户姓名列"Doe, John"),上面的简单脚本会把引号里的逗号当成列分隔符,直接用就会出错。这时候得用awk的FPAT功能来正确识别带引号的单元格:

BEGIN {
    # 定义单元格的格式:要么是不含逗号的字符串,要么是双引号包裹的字符串(内部可以有逗号)
    FPAT = "([^,]+)|(\"[^\"]+\")"
    OFS = ","
}
{
    merge_times = NF - 14
    for (i=1; i<=merge_times; i++) {
        # 合并时处理引号,避免出现"abc","def"变成"abc","def"这种冗余格式
        if ($10 ~ /^"/ && $11 ~ /"$/) {
            # 去掉中间重复的引号,合并成"abc,def"
            $10 = substr($10, 1, length($10)-1) "," substr($11, 2)
        } else {
            $10 = $10 "," $11
        }
        # 前移后面的列,减少列数
        for (j=11; j<NF; j++) {
            $j = $(j+1)
        }
        NF--
    }
    # 输出前检查:如果单元格含逗号但没引号,自动加上引号,保证CSV格式合法
    for (k=1; k<=NF; k++) {
        if ($k ~ /,/ && $k !~ /^"/) {
            $k = "\"" $k "\""
        }
    }
    print
}

运行方式和之前一样:

awk -f merge_csv_cols.awk your_data.csv

这个脚本的贴心之处:

  • FPAT能准确识别带引号的单元格,不会把内部的逗号当成列分隔符。
  • 合并带引号的单元格时,会自动处理重复的引号,保证合并后的单元格格式正确。
  • 输出前还会检查所有单元格,如果有含逗号但没引号的,自动补上引号,避免后续处理CSV时出错。

备选:用cut+paste组合(仅适合简单CSV)

如果你更习惯用基础的shell工具,也可以用cut和paste来实现,但这个方法只适合没有引号内逗号的情况,而且需要写个bash循环处理每行的列数差异,不如awk灵活:

while IFS=, read -ra cols; do
    col_count=${#cols[@]}
    if (( col_count <= 14 )); then
        echo "${cols[*]}" | tr ' ' ','
        continue
    fi
    # 提取前9列
    front_part="${cols[@]:0:9}"
    # 合并第10列到倒数第5列(因为要保留最后4列)
    merged_part="${cols[@]:9:$((col_count - 13))}"
    merged_part=$(echo "$merged_part" | tr ' ' ',')
    # 提取最后4列
    back_part="${cols[@]:$((col_count - 4)):4}"
    # 拼接输出
    echo "$front_part,$merged_part,$back_part" | tr ' ' ','
done < your_data.csv

不过说实话,这个方案写起来麻烦,处理边缘情况容易出错,还是更推荐用awk的方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:56:17