如何使用 sed 匹配由两个以上边界定义的多行范围?

How to Match Using sed a Multi-Line Range Defined by More Than Two Bounds?

我该如何...

...使用 sed 不仅可以匹配标准的 2 边界范围,还可以匹配以 "multi-range" 为特征的范围 - 某些特定的多个 (>2) 子匹配订单...

...WHILE 包含成对的内部匹配(例如 ( / ))可能与一对多范围的边界模式重叠?


工作简单范围匹配

假设我有一个模式,其内容在给定格式中类似于以下内容:

           someVar: SomeObj.someFunct
                        ("name1", "name2",
                         SomeConst)

在上述大多数情况下,无论某些常量 SomeObj.someFunct 的格式如何,我都可以使用范围模式进行捕获:

cat $file | sed -ne '/^[ \t]*[a-zA-Z0-9_\: ]*SomeObj.someFunct/,/)/ {p}'

双范围边界是:

/^[ \t]*[a-zA-Z0-9_\: ]*SomeObj.someFunct/

/)/

所以基本上它是在寻找开始位,然后是结束括号。


失败案例

但是如果在 ) 之间有东西,这偶尔会失败。一个简单的例子可能是:

           someVar: SomeObj.someFunct
                        (thisTimeImCallingTheFunction(), "name2",
                         SomeConst)

如果 SomeObj.someFunct 在下一行,它也会失败,即:

           someVar:
              SomeObj.someFunct("name1", "name2",
                         SomeConst)

乘法有界范围?

我的想法是,我想要一个 "multi-range",或者等价于:

/^[ \t]*[a-zA-Z0-9_\: ]*/

/SomeObj.someFunct/

/(/

|和(可选)
|
|一些任意数量的中介 ())

/)/

...因此范围必须至少检查 4 个边界,最后两个检查可选的中间值以防止内部 ( / ) 对。


我得到了什么

不幸的是,即使考虑到可选的内部匹配,这个天真的版本也没有....

cat $file | sed -ne '/^[ \t]*[a-zA-Z0-9_\: ]*/,/SomeObj.someFunct/,/(/,/)/ {p}'

无效。

似乎模式 /check1/,/check2/,/check3/,/check4/ 无效,必须采用一些更复杂的策略。


问题总结

  1. 检查由两个末端模式和内部模式以某种特定顺序定义的连续范围(但不一定在同一行)
  2. 每遇到一个 (,在范围 ).
  3. 的最终匹配之前跳过一个 )

Note/Disclaimer

我搜索了以前类似的问题,认为有人问过,但只找到了问题 asking about multiple ranges with standard dual bounds,而不是 "multi range" 在由两个以上的边界匹配定义的单个连续范围的意义上.


奖金问题:

一旦有了这样的多界匹配,我该如何多捕获以下模式:

  1. someVar之前的空格。
  2. 三个参数(包括interiror ( / ) in the complex case.

您的第一个嵌套函数调用失败案例无法在正确的 "regular" 表达式中处理。一些扩展,如 Perl 的 REs 可以处理查找任意嵌套事物的匹配对,但实际上您此时需要一个实际的解析器。

这可能适合您 (GNU sed):

sed -rn '/^\s*\w+:/!b;:a;N;/\)\s*$/!ba;/^\s*\w+:\s*SomeObj\.someFunct\s*\(/p;//d;D' file

这匹配第一个变量,然后附加更多行,直到 ) 后跟可能的白色 space 和行尾。然后检查模式 space 以查看是否满足所有其他匹配项,如果满足则打印模式 space。否则删除第一行并重复该过程。

N.B。如前所述,正则表达式不能在所有情况下都用于匹配括号,这应该留给解析器处理。

sed 用于 s/old/new,仅此而已。如果您要用它做任何其他事情,那么您使用的结构在 1980 年代发明 awk 时就已经过时了。使用 GNU awk 进行多字符 RS 和第三个参数匹配():

$ cat tst.awk
BEGIN { RS="^$" }
match([=10=],/([[:blank:]]*\w*:\s*SomeObj\.someFunct\s*\()(.*)/,a) {
    str = a[1]
    while ( match(a[2],/([^\)]*\))(.*)/,a) ) {
        str = str a[1]
        if ( !index(a[1],"(") ) {
            break
        }
    }
    print str
}

$ awk -f tst.awk file
           someVar: SomeObj.someFunct
                        (thisTimeImCallingTheFunction(), "name2",
                         SomeConst)

上面找到一个与您的起始正则表达式匹配的字符串,然后将以 ) 结尾的字符串附加到该字符串,直到它到达一个以 ) 结尾且不包含 [=13= 的字符串].