首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用BASH_REMATCH捕获重复模式的最长匹配

如何使用BASH_REMATCH捕获重复模式的最长匹配
EN

Stack Overflow用户
提问于 2021-02-03 06:08:00
回答 3查看 128关注 0票数 1

我正在尝试捕捉重复模式中最长的匹配。

代码语言:javascript
复制
do_run() {
    local regex='.*((abc)+).*'
    local str='_abcabcabc123_'

    echo "regex=${regex}"$'\n'
    echo "str=${str}"$'\n'

    if [[ "${str}" =~ ${regex} ]]
    then
        for i in ${!BASH_REMATCH[@]}
        do
            echo "$i=${BASH_REMATCH[i]}"
        done
    else
        echo "no match"
    fi
}

我得到以下输出:

代码语言:javascript
复制
regex=.*((abc)+).*
str=_abcabcabc_
0=_abcabcabc123_
1=abc
2=abc

我试着弄到这样的东西:

代码语言:javascript
复制
regex=.*((abc)+).*
str=_abcabcabc123_
0=_abcabcabc123_
x=abcabcabc

(更新:x只是表示匹配组的索引并不重要,但我需要知道检索匹配组的数字.)

更新:

阅读后评论,下面的正则表达式将工作:((abc)+)

但是,我还需要捕捉到前面的内容和((abc)+)之后的内容。

我之前没有提过,因为我认为同样的解决方案也会适用。

所以新的代码是:

代码语言:javascript
复制
do_run() {
    local regex='(.*)((abc)+)(.*)'
    local str='_abcabcabc123_'

    echo "regex=${regex}"$'\n'
    echo "str=${str}"$'\n'

    if [[ "${str}" =~ ${regex} ]]
    then
        for i in ${!BASH_REMATCH[@]}
        do
            echo "$i=${BASH_REMATCH[i]}"
        done
    else
        echo "no match"
    fi
}

然后得到以下输出:

代码语言:javascript
复制
regex=(.*)((abc)+)(.*)
str=_abcabcabc123_
0=_abcabcabc123_
1=_abcabc
2=abc
3=abc
4=123_

我希望能够从一个匹配的组中检索abcabcabc,但也希望能够检索它之前的内容和后面的内容。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-02-03 08:23:55

作为一种解决办法,您可以这样做:

代码语言:javascript
复制
[STEP 101] $ cat foo.sh
v=_abcabcabc123_
if [[ $v =~ (abc)+ ]]; then
    middle=${BASH_REMATCH[0]}

    [[ $v =~ (.*)"$middle" ]]
    before=${BASH_REMATCH[1]}

    [[ $v =~ "$middle"(.*) ]]
    after=${BASH_REMATCH[1]}

    echo "before: $before"
    echo "middle: $middle"
    echo "after : $after"
fi
[STEP 102] $ bash foo.sh
before: _
middle: abcabcabc
after : 123_
[STEP 103] $
票数 1
EN

Stack Overflow用户

发布于 2021-02-03 08:15:36

我还需要捕捉前面的内容和后面的内容((abc)+)。

为此,您通常需要对perl ( (?<!abc)((abs)+)(.*)中的某些内容)进行负面展望。

我不擅长perl regex,在启用perl的grep中,我能够做到这一点:

代码语言:javascript
复制
$ grep -oxP '(.*)(?<!abc)((abc)+)\K(.*)' <<<'_abcabcabc123_'
123_
$ grep -oP '((abc)+)' <<<'_abcabcabc123_'
abcabcabc
$ rev  <<<'_abcabcabc123_' | grep -oP '(.*)(?<!cba)((cba)+)\K(.*)' | rev
_

Bash没有外观,也没有perl正则表达式。考虑使用python或perl。

但是,您可以通过将regex上的部分拆分,然后读取行来使用sed,这可能更简单:

代码语言:javascript
复制
$ readarray -t lines < <(<<<'_abcabcabc123_' sed -E 's/((abc)+)/\n&\n/'); declare -p lines
declare -a lines=([0]="_" [1]="abcabcabc" [2]="123_")

另一个想法:您可以使用bash展开将abc部件替换为唯一的部件,然后将其拆分到该分隔符上:

代码语言:javascript
复制
$ IFS=' ' read -r before post < <(printf "%s\n" "${str//abc/ }") ; declare -p before post
declare -- before="_"
declare -- post="123_"
# or
$ IFS='@' read -r before post < <(<<<"${str//abc/@}" tr -s '@') ; declare -p before post
declare -- before="_"
declare -- post="123_"
票数 1
EN

Stack Overflow用户

发布于 2021-02-03 08:40:38

对于给定的输入,此正则表达式将有效:

代码语言:javascript
复制
re='^([^a]|a[^b]*|ab[^c]*)((abc)+)(.*)'
str='_abcabcabc123_'
[[ $str =~ $re ]] && declare -p BASH_REMATCH

输出:

代码语言:javascript
复制
declare -ar BASH_REMATCH=([0]="_abcabcabc123_" [1]="_" [2]="abcabcabc" [3]="abc" [4]="123_")

所以你可以使用:

代码语言:javascript
复制
"${BASH_REMATCH[1]}" # string before
"${BASH_REMATCH[2]}" # string containing all "abc"s
"${BASH_REMATCH[4]}" # string after

RegEx演示

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66022130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档