Bash将CSV字符串读入数组时如何保留空字段
问题根源
原有方案通过awk将CSV每个字段单独输出为一行,再通过arr=($(csv_to_lines $line))的命令替换方式给数组赋值,这种写法下Shell会对命令输出做单词分割,所有空行会被直接丢弃,最终导致CSV里的空字段全部丢失。比如测试用例line='A,,,,,"C,D",E'执行后只会得到3个有效元素,中间的5个空字段会全部消失。
修复方案
核心是放弃默认的命令替换+单词分割的赋值逻辑,改用可以逐行读取、完整保留空行的方式接收命令输出,推荐用Bash内置的mapfile(别名readarray)实现:
function csv_to_lines() { echo "$@" | awk ' BEGIN {FPAT = "([^,]*)|(\"[^\"]+\")";} {for(i=1; i<=NF; i++) {printf("%s\n", $i)}}' } line='A,,,,,"C,D",E' # -t 参数表示自动去除每行末尾的换行符,通过进程替换传递命令输出 mapfile -t arr < <(csv_to_lines "$line") # 验证输出,结果为 A,,,,,"C,D",E, 和预期完全一致 printf '%s,' "${arr[@]}"
注意事项
- 必须使用进程替换
< <(command)给mapfile传值,如果用管道csv_to_lines "$line" | mapfile -t arr的写法,mapfile会运行在子Shell中,外部作用域无法拿到赋值后的数组。 - 所有变量、参数传递时要加双引号,比如
echo "$@"、csv_to_lines "$line",避免包含空格、特殊字符的字段被Shell提前拆分。 - 如果使用4.0以下的老旧Bash版本(没有内置
mapfile),可以用while循环逐行读取的兼容写法,效果完全一致:
arr=() while IFS= read -r field; do arr+=("$field") done < <(csv_to_lines "$line")
内容的提问来源于stack exchange,提问作者Tohiko
相关产品推荐
相关产品推荐

