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

Linux/Posix环境下Bash解析含引号内逗号的多行CSV至变量

用POSIX默认工具解析含引号内逗号的CSV并逐行存变量

问题分析

直接依赖IFS=,分割CSV会误将引号内的逗号识别为字段分隔符,破坏字段结构;使用awk虽能完成解析,但需要解决字段向shell变量传递、指定行号处理的问题。

解决方案:shell内置工具+awk配合实现

1. 核心思路

用awk完成CSV的合规解析(识别引号内逗号为字段内容而非分隔符),通过**空字符(\0)**作为字段输出分隔符(CSV标准字段不会包含空字符,避免冲突),再由shell逐行读取并分割字段存入变量,同时awk可指定处理特定行号。

2. 完整代码示例

假设示例CSV内容如下:

id,name,company,email
1,John Doe,"AC XA, S.A.",john@example.com
2,Jane Smith,"Tech Corp, Inc.",jane@example.org

处理脚本:

#!/bin/sh

# 处理第2行(跳过表头),如需处理所有行则删除NR==2条件;跳过表头用NR>1
awk -F ',' '
NR==2 {
    n = 0
    in_quote = 0
    current = ""
    for (i=1; i<=NF; i++) {
        current = current $i
        # 统计当前片段的引号数量
        gsub(/"/, "", temp = current)
        quote_count = length(current) - length(temp)
        
        if (quote_count % 2 != 0) {
            in_quote = !in_quote
            if (!in_quote) {
                # 引号闭合,清理字段首尾引号后存入数组
                sub(/^"/, "", current)
                sub(/"$/, "", current)
                fields[++n] = current
                current = ""
            } else {
                # 引号未闭合,拼接逗号继续读取后续片段
                current = current ","
            }
        } else {
            if (!in_quote) {
                # 无引号或引号已闭合,清理后存入数组
                sub(/^"/, "", current)
                sub(/"$/, "", current)
                fields[++n] = current
                current = ""
            } else {
                # 引号内的逗号,直接拼接
                current = current ","
            }
        }
    }
    # 用空字符分隔字段输出,确保字段含空格/特殊字符时不被拆分
    for (i=1; i<=n; i++) {
        printf "%s%c", fields[i], (i == n ? "\n" : "\0")
    }
}' sample.csv | while IFS=$'\0' read -r id name company email; do
    # 后续处理逻辑示例
    echo "ID: $id"
    echo "Name: $name"
    echo "Company: $company"
    echo "Email: $email"
    echo "---"
done

3. 纯shell实现(无awk依赖)

若需完全依赖shell内置命令,可通过逐字符跟踪引号状态实现解析:

#!/bin/sh

while IFS= read -r line; do
    # 跳过表头行
    if [ "$line" = "id,name,company,email" ]; then
        continue
    fi

    in_quote=0
    fields=()
    current=""
    # 逐字符遍历行内容
    for ((i=0; i<${#line}; i++)); do
        char="${line:i:1}"
        if [ "$char" = '"' ]; then
            in_quote=$((1 - in_quote))
            continue # 跳过引号本身,不存入字段
        fi
        if [ "$char" = ',' ] && [ $in_quote -eq 0 ]; then
            # 非引号内的逗号,分割字段
            fields+=("$current")
            current=""
        else
            current="$current$char"
        fi
    done
    # 加入最后一个字段
    fields+=("$current")

    # 字段存入变量
    id="${fields[0]}"
    name="${fields[1]}"
    company="${fields[2]}"
    email="${fields[3]}"

    # 后续处理逻辑示例
    echo "ID: $id"
    echo "Name: $name"
    echo "Company: $company"
    echo "Email: $email"
    echo "---"
done < sample.csv

这种方法无需外部工具,但逻辑繁琐,仅适合字段无换行的简单CSV场景。

注意事项

  • 若CSV包含多行字段(字段内有换行),纯shell的read会截断内容,此时优先选择awk方案,awk可读取多行直到引号闭合。
  • 确保CSV格式合规:字段内引号成对出现,无未闭合的引号。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:03:14