我的任务是从一个非常难看的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将如何知道什么是价值突破以及下一个领域是什么。
感激不尽的任何帮助。
答案 0 :(得分:1)
在最坏的情况下(例如,如果, command2=
可能出现在另一个键的引用值中),唯一的办法就是为这种讨厌的格式编写一个专用的解析器。 (不幸的是,杀死提出它的人不会解决任何问题,并且可能会产生新问题。我知道它可能很诱人,但不会。)
对于快速而肮脏的黑客攻击,也许这就足够了:
grep -oP '(^|, )command2=\K([^,"]+|"[^"]+")'
如果引用字段值,这将保留双引号,但如果不需要,则应该很容易修复。但是,移动到比grep
更好的工具可以带来更好的精度;这是一个sed
变种,附加锚定:
sed -n 's/^\(.*, \)*command2=\(\((^,"]*\)\|"\([^"]*\)"\)\(, .*\)*$/\4\5/p'
答案 1 :(得分:1)
我会将grep
和sed
结合起来。假设您在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语句以适应,应该是显而易见的......