【问题标题】:bash_rematch and regex (with nested parens)bash_rematch 和正则表达式(带有嵌套括号)
【发布时间】:2026-02-04 07:55:01
【问题描述】:

我在使用正则表达式时遇到问题,我需要搜索并删除与正则表达式匹配的模式,当发现我需要修剪时。我写了一个这样的正则表达式

regex='(.*)((aa[[:space:]]bb)|(awd)|(bab)|(bc[[:space:]]d))(*.)'

我定义了所有的开头(1),可以是目标的部分(2)和所有的结尾(3)。 使用像 (.)(abc)(.) 这样的简单正则表达式很容易 字符串="abc";正则表达式='(.)(abc)(.)'

[[ $string =~ $regex) && myvar=${BASH_REMATCH[2]} && buffer=${BASH_REMATCH[1]}${BASH_REMATCH[3]}

当我用嵌套的括号和 OR 组定义正则表达式时,问题就开始了,就像这里发布的第一个正则表达式一样。 这是我的 shell 中的一个示例:

$ string=" foo bar baz bac"
$ regex='(.*)((hello[[:space:]]world)|(example)|(funk[[:space:]]you)|(bar[[:space:]]baz))(.*)'

$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[1]}
foo
$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[2]}
bar baz
$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[3]}

$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[4]}

$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[5]}

$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[6]}
bar baz
$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[7]}
bac
$ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[@]}
foo bar baz bac foo bar baz bar baz bac

匹配有一个奇怪的行为,我没有在 ${BASH_REMATCH[3]} 中找到输入字符串的其他部分,尽管它位于正则表达式的第三个括号中。 嵌套括号会发生什么?

【问题讨论】:

    标签: regex bash match


    【解决方案1】:

    bash 根据左括号从左到右的顺序为捕获组分配编号。基本上,它是深度优先排序,而不是您假设的广度优先排序。

    1. (.*)
    2. (
    3.   (hello[[:space:]]world)|
    4.   (example)|
    5.   (funk[[:space:]]you)|
    6.   (bar[[:space:]]baz)
       )
    7. (.*)
    

    在这个正则表达式中,第 2 组本质上是第 3、4、5 或 6 组中实际匹配的任何一个的副本,因为第 2 组不包含任何其他内容。第 7 组是您认为的第三个括号组。

    第 0 组是整个比赛,这解释了您使用 @ 的最后一行:

    $ [[ $string =~ $regex ]] && echo ${BASH_REMATCH[@]}
    foo bar baz bac foo bar baz bar baz bac
    |             | | | |     | |     | | |
    +-------------+ +-+ +-----+ +-----+ +-+
           0         1     2       6     7
    

    (在分词过程中,空组 3、4 和 5 被吞并为空格。)

    【讨论】:

    • 我不明白为什么模式“bar baz”匹配 2 次,在同一个例子中,目标在第 2 组和第 5 组,在这个例子中,目标在第 2 组和第 6 组。有什么变化从一个实例到另一个实例?我总是使用相同的正则表达式 (.*)((foo)|(bar)|(baz))(.*)
    • 我不确定你在问什么。那是一个不同的正则表达式;你匹配的是什么字符串(或字符串)?
    • bar baz 匹配两次,因为它明确包含在第 6 组中,而在第 2 组中,因为第 2 组包含第 6 组。
    • 您的困惑可能部分是因为bash 没有非捕获组。无论您是否愿意,都会自动捕获任何带括号的正则表达式。
    • 另一个正则表达式,例如另一个模式配置,hello world | aaabb |样品 |巴兹
    最近更新 更多