sed 匹配语法块

目录

   基于一个实际的问题,就是我想在 nginx 配置的一个 location 块后面添加另外一个 location 块,本来想用 sed 去做这个,但是突然发现很困难。
经过一段时间思考还是解决了这个问题,现在记录一下。
    我利用 sed 写了一个小工具可以像 grep 一样使用,但是过滤的是一个文本块 https://github.com/jingminglang/grepblock

示例配置

#这里是一个假想的 nginx 配置 我们现在的目标是在 location =/ {...} 这个块的后面添加一个 location /c {} 这样的块
jimila@CDYJY-JINGML:testsed$ cat nginx.conf 

location /b/ {
   a
   a
   a
   if(a) {
}
   a
   if(a) {
   b
   c
   d
}
   a
   a
}
location =/ {
   a
   a
   a
   if(a) {
}
   a
   if(a) {
   b
   c
   d
}
   a
   a
}

location / {
   a
   a
   a
   c
   c
   c
   c
   c
   if(a) {
   }
   a
   a
}

困难在哪里

   我们面对的最主要的困难是我们很难用 sed 去匹配一个语法块 比如 location =/ {...} 中间可能嵌套 if(){}这样的语法块
如果只用简单的/location =\//,/}/ 这样简单的范围匹配肯定不能达到我们的目的。
如何对一个块进行匹配是我们是首先需要解决的。

降级问题

   不如我们先将这个复杂的问题降级为一个看起来不那么复杂的问题:如何检查 nginx.conf 中的{}是否是匹配的,如果匹配输出 match,如果不匹配输出 not match.
虽然这个问题的解法也不那么直观,但是好歹感觉上觉得没那么难了.

解决思路

   对于那个降级的问题,我想到一个方法把配置文件只保留{或},然后循环去掉{},如果最后什么都没剩下,那么证明我们的配置文件是匹配的,如果还剩下一些东西,证明配置文件是不匹配的。
举例说明: 
如果配置文件去掉其他字符是这样的:{{}{{}}},然后我们循环去掉{},过程会是这样{{}{{}}} -> {{}} -> {} -> '' ,最后检查发现什么都没剩下 所有是匹配的
如果配置文件去掉其他字符是这样的:{{{}}{}{},然后我们循环去掉{},过程会是这样{{{}}{}{} -> {{} -> { ,最后发现里面还有东西,所以是不匹配的

解决降级的问题

sed   -e ':a;N;s/\n//g;$!ta;s/[^{}]//g;:b;s/{}//g;tb;/^$/s/$/match/;tc;s/.*/not match/;:c' nginx.conf
简单解释一下:
:a;N;s/\n//g;$!ta 这一段做的事情就是读入文件所有内容并去掉\n,这时整个配置文件就变为了一行.
s/[^{}]//g 然后把所有除了{或}的内容去掉,这时配置文件就变成了{{}{}}{{}{}}{{}}
:b;s/{}//g;tb 然后循环的去掉{} ,最终我们的配置变成了什么都没有了
/^$/s/$/match/;tc 如果匹配到什么都没有了,就输出 match,并结束程序
s/.*/not match/;  否则就输出 not match

最终问题的解决

解决了降级后的问题,我们仔细思考一下就能得到我们想要的解决方案,这里给出程序
sed    '/location =\/ {/ {:c;N;H;s/[^{}]//g;:a;s/{}//g;ta;/^$/s/$/$/;tb;s/.*//g;x;bc;:b;g;s/^\n\+//g;s/\(.*\)/\1\nlocation \/c {}\n/g}' nginx.conf
location /b/ {
   a
   a
   a
   if(a) {
}
   a
   if(a) {
   b
   c
   d
}
   a
   a
}
location =/ {
   a
   a
   a
   if(a) {
}
   a
   if(a) {
   b
   c
   d
}
   a
   a
}
location /c {}


location / {
   a
   a
   a
   c
   c
   c
   c
   c
   if(a) {
   }
   a
   a
}



#这里虽然看起来复杂,但是一步步分解,就能明白(推荐使用 sedsed 这个工具去分析),我就不解释了


目录