根据上方的行选择行

根据上方的行选择行

我有一个项目列表,我想从中选择活动项目的名称:

item {
  status: "Active"
  properties {
    key_a: value
  }
  id: 42
  name: "Foo"
}
item {
  status: "Disabled"
  properties {
    key_b: value
  }
  id: 12
  name: "Bar"
}
item {
  status: "Active"
  id: 2
  name: "Baz"
}

我知道我可以使用捕获组来提取名称pcregrep

$ cat list.txt | pcregrep -o1 -i '^  name: "(.*)"'
Foo
Bar
Baz

使用 OR 表达式,我还可以获得重复状态值和名称的列表:

$ cat list.txt | pcregrep -o2 -i '^  (status|name): "(.*)"'
Active
Foo
Disabled
Bar
Active
Baz

最后,我需要根据前面的行过滤列表中的名称。我怎样才能做到这一点?

最终输出应该是:

Foo
Baz

答案1

我认为你不能grep单独使用变体来做到这一点(诚然我不知道pcregrep)。尝试awk

awk '/^ *status.*Active.$/ {ACT = 1} /^ *name:/ && ACT {gsub (/"/, "", $2); print $2; ACT = 0}' file
Foo
Baz

答案2

由于大部分繁重的工作已经由 完成pcregrep,您现在可以将它的 so/p 传递给这个简短的sed片段:

  sed -ne 'N;s/^Active\n//p'

这使得sed一次查看 2 行,而不是默认的 1 行。该N命令通过用换行符分隔将下一行粘贴到模式空间\n。现在,只有 sed 能够删除模式空间中的活动第一行,剩余的模式空间才会被打印。这是有条件的打印。啥也不做,并且-n应确保不会自动打印模式空间。 HTH。

答案3

你也可以使用 sed

sed '/status.*Active/,/name/!d;/name/!d;s/[^"]*"\([^"]*\)"/\1/' infile

答案4

您还可以使用范围运算符 ofPerl并使用 a 对其进行约束,以处理块中的boolean condition嵌套括号。{}

通常,人们会在Perlas中写入一个范围/re1/ ... /re2/,这将导致perl选择以 regex 开头/re1/并在满足 regex 的行上结束的块/re2/。我们可以进一步限制这一点,说:/re1/ ... /re2/ && $depth==0

这将导致perl仅选择那些具有深度为零的附加约束的块。就像在这种情况下一样,仅当}发现导致深度计数降至零时才会发生块结束,OTW,块累积也会继续超过此标记。

perl -lne '
    if ( /\{/ ... /\}/ && !$depth ) {
        if    ( /\{/ )                         { $depth = /^\h*item\h+\{\h*$/ ? 0 : ++$depth;     }
        elsif ( /\}/ )                         { print($name),undef($flag) if !$depth-- && $flag; }
        elsif ( /^\h*status:\h*"Active"\h*$/ ) { $flag = 1;                                       }
        elsif ( /^\h*name:\h/ )                { $name = (split /"/)[1];                          }
    }
' input.file

相关内容