【问题标题】:grep-ing for the value of a csv filegrep 获取 csv 文件的值
【发布时间】:2016-09-24 15:29:46
【问题描述】:

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

csv 格式如下:

command1=value1, command2=value2, etc etc.

到目前为止,我正在 grep-ing 查找所需的命令,然后通过 cut -f 2 -d '=' 管道返回值。

我遇到的问题是其中一个字段是文本,并且可以有多个值,这些值也用逗号分隔。要添加另一个曲线球,当(且仅当)其中一个值中有空格时,该字段将用双引号引起来,因此我要提取的值可能是:

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

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

我以为我已经通过简单地使用 grep -oP '(?

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

 command1=value1, command3=value3, command2=value2

如果 command2 可能用双引号括起来也可能不括在双引号中,我如何获取它的值,它也可能包含逗号。我正在努力看看它是如何可能的,因为 grep 将如何知道什么是值中断以及下一个字段是什么。

感激地接受任何帮助。

【问题讨论】:

  • 您所描述的内容听起来很容易用一个小而简单的 awk 脚本来处理,但您必须发布一些简洁、可测试的示例输入和预期的输出,以便我们能够为您提供帮助。

标签: bash csv grep


【解决方案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 库的真正编程语言。

【讨论】:

  • grep | sed 通常可以卓有成效地重构为 sed 脚本。
  • s///p,就像你的回答一样?
  • grep 'foo'sed '/foo/' 但在这种情况下,我会将 -e '/command2=/!d' 放在脚本的开头(删除不匹配的行)。
【解决方案2】:

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

不过,对于快速而肮脏的 hack,也许这已经足够了:

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

如果字段值被引用,这将保留双引号,但如果不希望这样做应该很容易修复。不过,使用比grep 更好的工具也可以带来更好的精度;这是一个带有额外锚定的sed 变体:

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

【讨论】:

    【解决方案3】:

    idk 是否是您要查找的内容,但鉴于此输入文件:

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

    这个 GNU awk(用于 split() 的第 4 个参数)脚本可能是您想要的:

    $ 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"
    

    修改打印语句以适应,应该很明显......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-16
      • 1970-01-01
      • 1970-01-01
      • 2015-06-14
      • 1970-01-01
      • 2018-05-29
      • 1970-01-01
      相关资源
      最近更新 更多