如何在Bash中解析CSV文件?


111

我正在研究一个很长的Bash脚本。我想将CSV文件中的单元格读取到Bash变量中。我可以解析行和第一列,但不能解析其他任何列。到目前为止,这是我的代码:


  cat myfile.csv|while read line
  do
    read -d, col1 col2 < <(echo $line)
    echo "I got:$col1|$col2"
  done

它仅打印第一列。作为附加测试,我尝试了以下操作:

read -d, x y < <(echo a,b,)

$ y是空的。所以我尝试了:

read x y < <(echo a b)

$ y是b。为什么?


7
你有没有考虑awk使用$1$2等等?
BeemerGuy 2010年

4
作为旁注:command <<(echo“ string”)---> command <<<“ string”
tokland 2010年

1
“ cut”命令行程序是为此目的而设计的:ss64.com/bash/cut.html
Jay

Answers:


214

您需要使用IFS而不是-d

while IFS=, read -r col1 col2
do
    echo "I got:$col1|$col2"
done < myfile.csv

请注意,对于常规用途的CSV解析,您应该使用专门的工具,该工具可以处理带有内部逗号的带引号的字段,以及Bash无法自行处理的其他问题。此类工具的示例为cvstoolcsvkit


7
提议的解决方案适用于非常简单的CSV文件,也就是说,如果标头和值没有逗号和嵌入的引号,则该解决方案很好。编写通用的CSV解析器实际上非常棘手(特别是因为有多个CSV“标准”)。使CSV文件更适合* nix工具的一种方法是,例如使用Excel将其转换为TSV(制表符分隔的值)。
达到峰值

有趣的是我无法在体内做mkdir。我正在command not found。只有echo作品。
Zsolt

1
@Zsolt:没有理由应该是这种情况。您必须有错字或错字的非印刷字符。
暂停,直到另行通知。

2
@DennisWilliamson您应将分隔符括起来,例如在使用时;while IFS=";" read col1 col2; do ...
thomas.mc.work,

1
@ thomas.mc.work:对于分号和其他shell特殊字符,这是正确的。在逗号的情况下,这是没有必要的,我倾向于忽略不必要的字符。例如,您始终可以使用花括号(例如${var})来指定用于扩展的变量,但在不必要时会省略它们。对我来说,看起来更干净。
暂停,直到另行通知。

10

man页面:

-d delim delim的第一个字符用于终止输入行,而不是换行符。

您正在使用-d,它将终止逗号上的输入行。它不会读取该行的其余部分。这就是$ y为空的原因。


3

我们可以用带引号的字符串来解析csv文件,并用say |分隔。用下面的代码

while read -r line
do
    field1=$(echo $line | awk -F'|' '{printf "%s", $1}' | tr -d '"')
    field2=$(echo $line | awk -F'|' '{printf "%s", $2}' | tr -d '"')

    echo $field1 $field2
done < $csvFile

awk将字符串字段解析为变量,tr删除引号。

对于每个字段执行awk时,速度稍慢。


1
好,您也可以使用昏迷(,)
pkarc

0

如果您想读取包含几行内容的CSV文件,那么这是解决方案。

while IFS=, read -ra line
do 
    test $i -eq 1 && ((i=i+1)) && continue
    for col_val in ${line[@]}
    do
        echo -n "$col_val|"                 
    done
    echo        
done < "$csvFile"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.