【问题标题】:Extract Matching Lines after first match第一次匹配后提取匹配行
【发布时间】:2017-11-30 21:54:07
【问题描述】:

我在命令行中有被分成“记录”的文本数据,每个记录都有相同的值(始终为 1)。在每条记录中,每一行都是一个单独的键和值(不幸的是,这不在 json 中)。键有时会在记录中重复,有时键名称是较长键的一部分。例如:

Record = 1
  Apple = 1
  Ball = 2
  Car = 3
    RedApple = 4
    Ball = 5
  Dog = 6
  Elf = 7
  Fudge = 8
Record = 1
  Apple = 2
  Ball = 4
  Car = 6
    RedApple = 8
    Ball = 10
  Dog = 12
  Elf = 14
  Fudge = 16
Record = 1
  Apple = 3
  Ball = 6
  Car = 9
    RedApple = 12
    Ball = 15
  Dog = 18
  Elf = 21
  Fudge = 24

是否有快速为每条记录获取一组键的行,只返回每个键的第一个结果?

例如:为每条记录获取键 {Apple, Ball, Dog}

将匹配以下行:

Record = 1
  Apple = 1
  Ball = 2
  Dog = 6
Record = 1
  Apple = 2
  Ball = 4
  Dog = 12
...

基本上,规则是在将一行与“记录”匹配后,获取下一个与“Apple”、“Ball”和“Dog”(间距表示精确键匹配)的唯一行并将这些行吐出。

我可以用 perl 写一些东西,而且不会太复杂。我不知道awk,所以不知道这样的东西是否更好。

【问题讨论】:

  • “我可以用 perl 写一些东西,而且不会太复杂” 那么您需要我们的帮助做什么呢?
  • 希望学习比多行perl脚本更好的方法
  • 您应该发布您的 Perl 并描述您遇到的问题。多行 Perl 程序没有任何问题。
  • 我明白了,但我不需要 perl 脚本的帮助,我知道我可以做到这一点。我正在看看是否可以学习更好的方法,例如单个命令行。
  • 那么你应该把它放在你的问题中。就目前而言,远不清楚你在问什么。单个命令行没有什么“更好”的了。如果您表现出诚意并发布了您编写的 Perl,它将帮助您获得更好的答案,尽管听起来您的问题属于 Code Review

标签: perl unix text awk grep


【解决方案1】:

是否有快速为每条记录获取一组键的行,每个键只返回第一个结果?

我不相信这实际上是你想要的。我相信您实际上想要在第二级标记为AppleBallDog 的项目,这意味着两者

Record = 1
  Apple = 1
  Ball = 2
  Car = 3
    RedApple = 4
    Ball = 5
  Dog = 6
  Elf = 7
  Fudge = 8

Record = 1
  Apple = 1
  Car = 3
    RedApple = 4
    Ball = 5
  Ball = 2
  Dog = 6
  Elf = 7
  Fudge = 8

应该产生

Record = 1
  Apple = 1
  Ball = 2
  Dog = 6

如果是这样,你可以使用

perl -ne'print if /^(?:\S|[ ]{2}(?:Apple|Ball|Dog)[ ]=)/'

grep -P '^(?:\S|[ ]{2}(?:Apple|Ball|Dog)[ ]=)'

输出:

Record = 1
  Apple = 1
  Ball = 2
  Dog = 6
Record = 1
  Apple = 2
  Ball = 4
  Dog = 12
Record = 1
  Apple = 3
  Ball = 6
  Dog = 18

使用方法见Specifying file to process to Perl one-liner

【讨论】:

  • 试过这个正则表达式,返回空结果集。谢谢
  • grep 版本出现错误。 (我已经对其进行了测试,发现了问题并修复了它,但修复并没有成为我的答案。)我的答案已经修复。
【解决方案2】:

如果这还不是你所需要的:

$ grep -E '^(Record|  (Apple|Ball|Car))' file
Record = 1
  Apple = 1
  Ball = 2
  Car = 3
Record = 1
  Apple = 2
  Ball = 4
  Car = 6
Record = 1
  Apple = 3
  Ball = 6
  Car = 9

然后编辑您的问题以显示更具代表性的示例。现在您已经接受了一个同样基于猜测您的需求的答案,并且可能比必要的更复杂(而这个可能更简单)。

【讨论】:

  • 以为我说得很清楚,但显然不是。不幸的是,这不起作用,因为前导空格的数量未知,所以我不能使用单个空格来排除行的第二个匹配项。
  • 再次重申,如果您需要帮助以提出最佳解决方案,请编辑您的问题以展示更具代表性的示例。从你到目前为止告诉我们的情况来看,我真的认为你目前接受的答案比必要的要复杂。
【解决方案3】:

awk 来救援!

$ awk '/^Record/ {h=$0; a["Apple"]=a["Dog"]=a["Ball"]=0}
       $1 in a   {if(h) {print h; h=""}
                  if(!a[$1]++) print}' file

Record = 1
  Apple = 1
  Ball = 2
  Dog = 6
Record = 1
  Apple = 2
  Ball = 4
  Dog = 12
Record = 1
  Apple = 3
  Ball = 6
  Dog = 18

说明保存标题行并重置计数。对于在必填键中具有第一个字段的行,打印标题一次并打印键第一次出现的行。

如果您只想提取第二级项目,则需要将前导空格作为键的一部分(以确定层次结构)。这可以是另一种选择...

$ awk -F' *= *' '/Record/ {h=$0; a["  Apple"]=a["  Dog"]=a["  Ball"]=0} 
                 $1 in a  {if(h) {print h;h=""}; if(!a[$1]++) print}'

【讨论】:

  • 太棒了!谢谢,从来没有学过太多 awk,但我可以理解这一点,基本上跟踪已经找到了哪些值。
  • 如果是这种情况,需要将 FS 设置为非空格(可能是“=”符号)并使用所需数量的空格作为键的一部分。
猜你喜欢
  • 2013-08-13
  • 2018-06-27
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 2018-02-17
  • 2022-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多