在bash正则表达式中匹配N个模式

时间:2018-10-02 11:56:01

标签: bash

我知道我可以做类似的事情:

[[ $s =~ ^(re)(re)$ ]]

用一系列括号表达式匹配填充BASH_REMATCH数组。但是可以匹配这些匹配中的未知数吗?例如,我得到以下信息:

s='abc defghi     jklm    nop '
[[ $s =~ ^([^ ]+ +)+$ ]]
declare -p BASH_REMATCH

输出:

declare -ar BASH_REMATCH=([0]="abc defghi     jklm    nop " [1]="nop ")

我认为每次匹配时都将括号表达式重写到BASH_REMATCH[1]上。

我真正想看到的是:

declare -ar BASH_REMATCH=([0]="abc defghi     jklm    nop " 
                          [1]="abc " 
                          [2]="defghi     " 
                          [3]="jklm    " 
                          [4]="nop ")

在单个命令中有可能吗?

1 个答案:

答案 0 :(得分:4)

很好,但是我认为您要的是未知数量的子表达式。

如下所示的循环可能是最好的选择:

s='abc defghi     jklm    nop '
a=()
t="$s"

while [[ $t =~ ^([^ ]+ +) ]]; do 
    a+=( "${BASH_REMATCH[1]}" )
    t="${t#${BASH_REMATCH[1]}}" 
done

declare -p a=()

输出:

declare -a a=([0]="abc " [1]="defghi     " [2]="jklm    " [3]="nop ")

在不损害原始字符串的情况下,这将从字符串开头剥离模式,然后将模式添加到结果数组,并使用“模式扩展”从字符串副本的开头剥离匹配项。当然,如果您希望它更美观,可以将其放在函数中;请记住,不能从函数返回数组,您需要使用全局数组或引用:

function ssplit() {
    local -n a="$1"
    local t="$2"
    while [[ $t =~ ^([^ ]+ +) ]]; do 
        a+=( "${BASH_REMATCH[1]}" )
        t="${t#${BASH_REMATCH[1]}}"
    done
}

declare -a foo=()

ssplit foo "$s"

declare -p foo

输出:

declare -a foo=([0]="abc " [1]="defghi     " [2]="jklm    " [3]="nop ")

请注意,local -n是bash版本4引入的功能。

另一种可能是基于对字符串的分析来构建匹配正则表达式:

x=( $s )
printf -v pat '%s' $(printf '%.0s([^[:space:]]+[[:space:]]+)' $(seq 1 "${#x[@]}"))
[[ $s =~ $pat ]] && declare -p BASH_REMATCH

输出:

declare -ar BASH_REMATCH=([0]="abc defghi     jklm    nop " [1]="abc " [2]="defghi     " [3]="jklm    " [4]="nop ")

我不太喜欢编写代码的代码,但这似乎行得通。尽管它不是单个命令,但至少可以避免while循环。请注意,在幕后,bash仍在遍历printf的参数以构建$pat