我正在尝试sed
从长文件(Junos 配置)中提取像这样的大括号分隔的配置块:
group foo {
command;
setting {
value;
}
command;
}
诀窍是停在}
与第一行缩进相同的地方。
我学会了如何使用sed
从一种模式到另一种模式的匹配,并尝试了这个:
$ sed -rn '/^( *)group foo/,/^\1\}/p' config.txt
sed: -e expression #1, char 41: Invalid back reference
问题是/^( *)group foo/
和/^\1\}/
是两个独立的模式,并且反向引用在它们之间不起作用吗?如果是这样,我怎样才能做到这一点?
答案1
你说得对:虽然反向引用在基本正则表达式中定义(布雷)(由于每个 sed 地址都是一个 BRE,它支持反向引用),反向引用无法检索另一个 BRE 中定义的捕获组。因此该地址中的捕获组/^( *)group foo/
无法被其他地址检索到/^\1\}/
。
这是test.awk
通过计算左大括号和右大括号来实现的:
brk && /\{/{brk++} #Increment brk if brk is not zero and line contains {
brk && /\}/{brk--} #Decrement brk if brk is not zero and line contains }
/^[[:blank:]]*group foo \{/{brk=1;prt=1} #Set brk and prt if match initial pattern
prt #Print line if prt is set
!brk && prt{prt=0} #If brk is zero and prt is not, set prt=0
$ cat file
foo bar
foo bar2
}
group foo {
command;
setting {
value;
}
command;
}
dri {
}
end
$ awk -f test.awk file
group foo {
command;
setting {
value;
}
command;
}
另一个不太优雅的选择依赖于计算空白空间,这就是您尝试背后的想法。如果缩进有制表符,它就会中断。
/^ *group foo \{/{
match($0,/^ */) #Sets RLENGTH to the length in characters of the matched string
i=RLENGTH
}
i #If i is set, the current line is printed
i&&/^ *\}$/{
match($0,/^ */) #Again, sets RLENGTH to the length of the matched string
if(RLENGTH==i){i=0} #If the value is equal to the one from group foo line, unset i
}
答案2
可以在 中使用反向引用/pattern/
,但它们不会从一个这样的表达式记住到另一个表达式。
sed 中有很多解决方案,例如(使用 GNU sed):
sed -rz 's@.*\n(( *)group foo.*\2}).*@\1@;s@^(( *).*)@\1\2@;s@(\n( *)}).*\2$@\1\n@' config.txt
该-z
标志用于加载模式空间中的整个配置。第一个删除最后一个右括号(贪婪*)s
开始之前和之后的所有内容,并带有适当的缩进。group foo
第二个s
将缩进复制到末尾。最后一个s
删除带有适当缩进的第一个右括号之后的所有内容。仅当有多个配置块与感兴趣的配置块处于同一缩进级别时才需要这最后两个命令。
答案3
sed
不为您提供跨模式使用反向引用的便利,但它确实允许您将两行放入单个模式空间中,然后查找反向引用。
$ sed -Ene '
/^\s+group foo \{$/,$!d
p;/^\s+group foo \{$/h;/\}/!d
G;/^(\s+)\S.*\n\1\S/q
' file
使用的 Sed 命令:
p
打印模式空间的内容。$!d
意味着删除该行,只要它不是最后一行。但是,这里使用范围运算符,因此意味着删除所有超出范围的行。范围是 foo 组行直到 eof。所以基本上它是跳过第一组 foo 行之前的所有行。G
将保留空间的内容附加到模式空间。q
意味着停止进一步处理。类似于退出。
另一种方法是首先识别起始行,然后继续打印并记录尾随大括号的嵌套深度,当嵌套深度达到零时停止。
$ sed -ne '/^\s*group foo \{$/,${
p;// {x;s/.*//;x;}
/\{/ {x;s/^/./;x;}
/\}/ {x;s/^.//;x;}
/\}/G;/\n$/q
}' file
perl
当你想要匹配空格时,使用它几乎是微不足道的。
$ perl -lne 'print if /^(\s+)(?{ $k=$1 })group\s+foo\s+\{/x ... /^$k\}/' file