我有一个项目列表,我想从中选择活动项目的名称:
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
嵌套括号。{}
通常,人们会在Perl
as中写入一个范围/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