正则表达式非贪婪地匹配多行直到以特定字符串开头的行

Regex to non-greedily match across multiple lines up to a line that starts with a specific string

我要自己回答这个问题,但这让我整天都不舒服,虽然在其他地方有解释,但我想我会 post 用我的解决方案来解决这个问题。

我遇到过需要替换一些跨越多行的文本的情况。不难找到有关如何跨多行匹配的主题。我的情况有点困难,因为我需要通配符匹配多行中的任何字符,直到停在第一个非缩进的结束括号处。

出于演示目的,我制作了一个示例文件,其中包含让我难以理解的功能:

起始文件:

cat << EOF > test.txt
server {
    abcdefg blablablabla
    pizza
    #blablablabla
    blablablabla {
    zazazazazaza
    }
    turtles
    #}
    ninjas
    blablablabla

} #comments that might or might not be here

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
}

zabzazab

EOF

这是我想要的输出。请注意,我要匹配的括号既不是右括号的第一个也不是最后一个。它唯一的显着特征是在我的比赛开始后成为一行开头的第一个 }

server {
    wxyz

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
} 

zabzazab

我希望能奏效。但是用 0777 slupring 去除了一行开头和结尾的标记,所以它不起作用:

~#  perl -0777 -pe 's/(abcdefg(.*?)(^}.*$))/wxyz/gs' test.txt
server {
    abcdefg blablablabla
    pizza
    #blablablabla
    blablablabla {
    zazazazazaza
    }
    turtles
    #}
    ninjas
    blablablabla

} #comments that might or might not be here

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
}

zabzazab

匹配线 start/end 同时 slupring 是症结所在:

~# perl -0777 -pe 's/(abcdefg(.*?)(}))/wxyz/gs' test.txt
server {
    wxyz
    turtles
    #}
    ninjas
    blablablabla

} #comments that might or might not be here

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
}

zabzazab


那么有没有办法让正则表达式匹配字符串和出现在行首的 { 的第一个实例?我也愿意使用 sed,但我认为我搜索的非贪婪性质会使 perl 成为更好的选择。

看来我需要 s 和 m 标志以及 slurping:

~# perl -0777 -pe 's/(abcdefg(.+?)(\n}))/wxyz/sm' test.txt
server {
    wxyz #comments that might or might not be here

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
}

我仍然不太明白为什么我需要 m 修饰符和 slurping。所以如果有人有更好的答案,我会标记那个而不是我自己的。

据我了解,您希望匹配字符串的一部分

server {
    abcdefg blablablabla
    pizza
    #blablablabla
    blablablabla {
    zazazazazaza
    }
    turtles
    #}
    ninjas
    blablablabla

} #comments that might or might not be here

server {
... blablablabla
}
...

"abcdefg" 开始并在行尾 "} #comments that might or might not be here" 结束,前提是 "abcdefg" 在缩进后开始一行并且该行前面是行 "server {".然后,您将用另一个字符串替换匹配的文本。

您可以使用以下正则表达式匹配要替换的文本:

/^server +\{\s+(abcdefg.+?\n\}.*?$)/sm

demo

标志 s 允许 .* 匹配换行符。标志 m 指示解析器将锚点 ^$ 分别视为一行的开头和结尾(大概与字符串的开头和结尾相反)。

我们可以在自由间距模式中编写正则表达式,使其自记录。

/
^server +\{\s+    # match 'server {` followed by 1+
                  #  whitespace chars
(                 # begin capture group 1
  abcdefg         # match literal
  .+?             # match 1+ chars, lazily
  \n              # match a newline
  \}              # match '}'
  .*?             # match 1+ chars, lazily
  $               # match end of line
)                 # end capture group 1
/smx              # single-line, multiline and free-
                  # spacing regex definition modes 

也许以下任何命令都可以做到

perl -0777 -pe 's/abcdefg.*?(\nserver.*?)/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?server/wxyz\n\nserver/s' text.txt
perl -0777 -pe 's/abcdefg.*?}.*?}.*?}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg(.*?}){3}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?\n}.*?\n/wxyz\n/s' test.txt

输出

server {
    wxyz

server {
    blablablabla
    blablablabla
    blablablabla
    blablablabla
}

zabzazab