将csv字符串读入bash数组

Posted

技术标签:

【中文标题】将csv字符串读入bash数组【英文标题】:Reading csv string into bash array 【发布时间】:2022-01-17 06:58:48 【问题描述】:

以下函数使用awk 将一个csv 行转换为多行。然后我可以将输出分配给一个数组,以便能够访问这些字段。

function csv_to_lines() 
echo $@ | awk '
BEGIN FPAT = "([^,]*)|(\"[^\"]+\")";
for(i=1; i<=NF; i++) printf("%s\n", $i)'


line='A,B,"C,D",E'
arr=($(csv_to_lines $line))

printf '%s,' "$arr[@]"

但是,这不适用于空字段。例如:

line='A,,,,,"C,D",E'
arr=($(csv_to_lines $line))

printf '%s,' "$arr[@]"

输出

A,"C,D",E,

但我期待

A,,,,,"C,D",E,

显然,当分配给数组时,所有空行都会被忽略。如何创建一个保留空行的数组?

【问题讨论】:

在 awk printf("%s\n", $i) = print $i。您的脚本中还有一些 shell 错误,将其复制/粘贴到 shellcheck.net 会告诉您。 【参考方案1】:

当前代码:

$ line='A,,,,,"C,D",E'
$ csv_to_lines $line
A




"C,D"
E

查看我们看到的实际生成的字符:

$ csv_to_lines $line | od -c
0000000   A  \n  \n  \n  \n  \n   "   C   ,   D   "  \n   E  \n
0000016

arr=(...) 将在空白处分割这些数据并将可打印的字符存储在数组中,实际上是这样做的:

$ arr=(A
"C,D"
E)
$ typeset -p arr
declare -a arr=([0]="A" [1]="C,D" [2]="E")

$ printf '%s,' "$arr[@]"
A,"C,D",E,

在数组中存储“空白行”的几个想法:

使用mapfile将每一行读入数组,例如:

$ mapfile -t arr < <(csv_to_lines $line)
$ typeset -p arr
declare -a arr=([0]="A" [1]="" [2]="" [3]="" [4]="" [5]="\"C,D\"" [6]="E")

或者让awk使用\n以外的东西作为分隔符,然后定义一个自定义的IFS将函数结果解析到数组中,例如:

$ function csv_to_lines()  echo $@ | awk '
BEGIN FPAT = "([^,]*)|(\"[^\"]+\")";
for(i=1; i<=NF; i++) printf("%s|", $i)'; 

$ csv_to_lines $line
A|||||"C,D"|E|

$ IFS='|' arr=($(csv_to_lines $line))
$ typeset -p arr
declare -a arr=([0]="A" [1]="" [2]="" [3]="" [4]="" [5]="\"C,D\"" [6]="E")

这两种情况都会导致:

$ printf '%s,' "$arr[@]"
A,,,,,"C,D",E,

【讨论】:

mapfile 解决方案是完美的。如果 csv 在其中一个字段中包含 |,则使用 | 之类的字符会遇到问题。

以上是关于将csv字符串读入bash数组的主要内容,如果未能解决你的问题,请参考以下文章

将文件中的行读入Bash数组[重复]

使用 bash 将多个匹配项读入数组

BASH - 从 csv 文件的行创建数组,其中第一个条目是数组名称

BASH - 如何从 CSV 文件中的列中提取数据并将其放入数组中?

如何避免作为 sql 查询输出的一部分返回的字符串值被拆分为 bash/shell 脚本中数组中的不同字段

将 Bash 数组转换为分隔字符串