grep-ing csv文件的值

时间:2016-05-26 14:40:34

标签: bash csv grep

我的任务是从一个非常难看的csv文件中提取某些值。

csv采用以下格式:

command1=value1, command2=value2, etc etc.

到目前为止没问题我正在寻找我需要的命令然后通过cut -f 2 -d' ='只返回价值。

我遇到的问题之一是字段之一是文本,可以有多个值,也用逗号分隔。要添加另一个曲线球,如果(并且仅当)其中一个值中有一个空格,则该字段将用双引号括起来,因此I' m想要拉的值可能是:

command=value,..
command=value1,value2,..
command="value 1",..
command="value 1, value 2",..

(其中..是日志文件中的其他值或行尾)

我以为我是通过使用grep -oP'(?< = command1 =)。*(?= command2)'简单地在两个字段名称之间拉数据来破解它的。然后通过转|管道切-c 2- |转。

但我现在发现字段显示的顺序并不一致,因此文件可能是:

 command1=value1, command3=value3, command2=value2

如果它可能包含或不包含在双引号中,我怎样才能得到command2的值,它也可能包含逗号。我很难看到它是如何可能的,因为grep将如何知道什么是价值突破以及下一个领域是什么。

感激不尽的任何帮助。

3 个答案:

答案 0 :(得分:1)

在最坏的情况下(例如,如果, command2=可能出现在另一个键的引用值中),唯一的办法就是为这种讨厌的格式编写一个专用的解析器。 (不幸的是,杀死提出它的人不会解决任何问题,并且可能会产生新问题。我知道它可能很诱人,但不会。)

对于快速而肮脏的黑客攻击,也许这就足够了:

grep -oP '(^|, )command2=\K([^,"]+|"[^"]+")'

如果引用字段值,这将保留双引号,但如果不需要,则应该很容易修复。但是,移动到比grep更好的工具可以带来更好的精度;这是一个sed变种,附加锚定:

sed -n 's/^\(.*, \)*command2=\(\((^,"]*\)\|"\([^"]*\)"\)\(, .*\)*$/\4\5/p' 

答案 1 :(得分:1)

我会将grepsed结合起来。假设您在example.csv

中有此输入
command1=value1, command2=value2,
command1=value1, command2="value2, value3"
command1=value1, command3=valu3

然后这个命令:

 grep 'command2=' example.csv |
  sed -e 's/.*command2=//g' -e 's/^\([^"][^,]*\),.*$/\1/g' -e 's/^"\([^"]*\)".*$/\1/g'

会给你这个:

value2
value2, value3

说明:

  • grep找到合适的行
  • sed中的第一个表达式(即第一个-e)会删除所需值之前的所有内容
  • 第二个表达式处理不带引号的案例
  • 第三个表达式用引号
  • 处理

请注意,CSV格式非常复杂。这个正则表达式做了一些假设,例如command2仅作为键出现。如果这个csv不够好,那么我会使用一个拥有成熟csv库的真正的编程语言。

答案 2 :(得分:0)

idk如果你正在寻找或不是,但给出了这个输入文件:

$ cat file
command1=value1.1,command2=value2.1,value2.2,command3="value 3.1",command4="value 4.1, value 4.2"

这个GNU awk(对于第4个arg到split())脚本可能是你想要的:

$ cat tst.awk
{
    delete(c2v)
    split($0,f,/,?[^=,]+=/,s)
    for (i=1; i in s; i++) {
        gsub(/^,|=$/,"",s[i])
        print "populating command name to value array:", s[i], "->", f[i+1]
        c2v[s[i]] = f[i+1]
    }
    print c2v["command2"]
    print c2v["command4"]
}

$ awk -f tst.awk file
populating command to value: command1 -> value1.1
populating command to value: command2 -> value2.1,value2.2
populating command to value: command3 -> "value 3.1"
populating command to value: command4 -> "value 4.1, value 4.2"
value2.1,value2.2
"value 4.1, value 4.2"

修改print语句以适应,应该是显而易见的......