Bash shell脚本 - csv解析

tin*_*ime 34 csv bash shell

我正在尝试解析包含可能100k +行的CSV.这是我的标准:

  1. 标识符的索引
  2. 标识符值

我想检索CSV中具有给定索引中给定值的所有行(用逗号分隔).

任何想法,特别考虑性能?

And*_*ikh 46

作为cut- 或awk基于单行的替代方案,您可以使用专门的csvtoolaka ocaml-csv:

$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"
Run Code Online (Sandbox Code Playgroud)

根据文档,它处理转义,引用等.

  • 我认为csvtool是我最好的朋友.疯狂以为我试图用bash解析.csv文件.替换了大量的bash行来处理解析和转义引号,嵌入式逗号等,只需调用一次csvtool! (5认同)
  • 为了得到这个东西:在ubuntu等上`sudo apt-get install csvtool`......然后`csvtool --help`作为手册页,erm,稀疏。 (2认同)

小智 42

请参阅此youtube视频:使用CSV文件的BASH脚本课程10

CSV文件:

Bob Brown;Manager;16581;Main
Sally Seaforth;Director;4678;HOME
Run Code Online (Sandbox Code Playgroud)

Bash脚本:

#!/bin/bash
OLDIFS=$IFS
IFS=";"
while read user job uid location
 do

    echo -e "$user \
    ======================\n\
    Role :\t $job\n\
    ID :\t $uid\n\
    SITE :\t $location\n"
 done < $1
 IFS=$OLDIFS
Run Code Online (Sandbox Code Playgroud)

输出:

Bob Brown     ======================
    Role :   Manager
    ID :     16581
    SITE :   Main

Sally Seaforth     ======================
    Role :   Director
    ID :     4678
    SITE :   HOME
Run Code Online (Sandbox Code Playgroud)

  • 这看起来并没有正确处理引用值(如"鲍勃布朗";"经理"; 16581;"主要"`或甚至`"鲍勃布朗";"经理;主任"; 16581;"主要"`) (3认同)

unw*_*ind 28

第一个原型使用普通旧grepcut:

grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}
Run Code Online (Sandbox Code Playgroud)

如果它足够快并且提供适当的输出,那么你就完成了.:)

  • +1。此管道不允许冒号转义 (`\:`) 或字符串引用 (`"foo: bar"`)。但这是解决问题的好方法和简单方法。 (2认同)
  • 虽然答案对于某些 CSV 文件是正确的,但恕我直言,它比帮助更具破坏性,因为它鼓励 SO 上的人们更喜欢“一行”命令并愉快地采用它们,而没有意识到与这些命令相关的问题(答案没有警告这些问题)以及)。简而言之,您使用特定的文件格式解析器解析某种文件格式。就像您不使用正则表达式来验证 html,而是使用 html 解析器/验证器一样。事实上,这种“单行文字”适用于这些文件格式的某些特殊情况,应始终以粗体/下划线字母表示。 (2认同)

bob*_*nce 12

CSV并不那么简单.根据您拥有的数据限制,您可能不得不担心引用的值(可能包含逗号和换行符)和转义引号.

因此,如果您的数据受到足够的限制,可以通过简单的逗号分割来解决问题,那么shell脚本可以轻松实现.另一方面,如果你需要正确解析CSV,那么bash不是我的首选.相反,我会看一个更高级别的脚本语言,例如带有csv.reader的 Python .


Hai*_* Vu 9

在CSV文件中,每个字段用逗号分隔.问题是,字段本身可能有一个嵌入的逗号:

Name,Phone
"Woo, John",425-555-1212
Run Code Online (Sandbox Code Playgroud)

您确实需要一个提供强大CSV支持的库包,而不是依赖于使用逗号作为字段分隔符.我知道像Python这样的脚本语言有这样的支持.但是,我对Tcl脚本语言感到满意,这就是我使用的.这是一个简单的Tcl脚本,可以满足您的要求:

#!/usr/bin/env tclsh

package require csv 
package require Tclx

# Parse the command line parameters
lassign $argv fileName columnNumber expectedValue

# Subtract 1 from columnNumber because Tcl's list index starts with a
# zero instead of a one
incr columnNumber -1

for_file line $fileName {
    set columns [csv::split $line]
    set columnValue [lindex $columns $columnNumber]
    if {$columnValue == $expectedValue} {
        puts $line
    }   
}
Run Code Online (Sandbox Code Playgroud)

将此脚本保存到名为csv.tcl的文件中,并将其调用为:

$ tclsh csv.tcl filename indexNumber expectedValue
Run Code Online (Sandbox Code Playgroud)

说明

该脚本逐行读取CSV文件并将该行存储在变量$ line中,然后将每行拆分为列列(变量$ columns).接下来,它选择指定的列并将其分配给$ columnValue变量.如果匹配,请打印出原始行.


Nat*_*ohl 8

使用awk:

export INDEX=2
export VALUE=bar

awk -F, '$'$INDEX' ~ /^'$VALUE'$/ {print}' inputfile.csv
Run Code Online (Sandbox Code Playgroud)

编辑:根据Dennis Williamson的优秀评论,通过使用-v开关定义awk变量,可以更清晰(和安全)地编写:

awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' inputfile.csv
Run Code Online (Sandbox Code Playgroud)

Jeez ......有变量和一切,awk几乎是一种真正的编程语言 ......

  • 出口可能是不必要的.并且你应该使用`awk's'变量传递功能,否则引用会变得毛茸茸:`awk -F,-v index = $ INDEX -v value = $ VALUE'$ index == value {print}'inputfile.csv` (3认同)
  • 这不会处理带有可能包含换行符的带引号字段的重要 CSV 文件。 (2认同)

D B*_*Bro 5

对于数据不包含任何特殊字符的情况,Nate Kohl 和 ghostdog74 建议的解决方案是好的。

如果数据在字段中包含逗号或换行符,awk 可能无法正确计算字段编号,并且您会得到不正确的结果。

在我编写的名为 csvquote 的程序(可在https://github.com/dbro/csvquote 上找到)的帮助下,您仍然可以使用 awk :

csvquote inputfile.csv | awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' | csvquote -u
Run Code Online (Sandbox Code Playgroud)

该程序在带引号的字段中查找特殊字符,并临时用不会混淆 awk 的非打印字符替换它们。然后他们在 awk 完成后恢复。