Bash 正则表达式和 IFS 拆分

Bash 正则表达式和 IFS 拆分

我有以下问题:我想从字符串中提取括号内的文本(带或不带括号)。我的字符串看起来像这样:

STR="[1] [2][345] [678 9] foo bar"

我最初想使用 bash 正则表达式和 BASH_REMATCH。我最终使用了以下代码:

regex='\[([^\]]*)\](.*)'
MATCHES=()
STR="[1] [2][345] [678 9] foo bar"
while [[ -n $STR && $STR =~ $regex ]];
do
    MATCHES+=("${BASH_REMATCH[1]}")
    STR=${BASH_REMATCH[2]}
    echo -e "matches: ${BASH_REMATCH[1]} -> ${BASH_REMATCH[2]}"
done

这种方法有效,但我的问题是它只会捕获括号内的一个字符,因此[345]会导致3.

我不明白为什么会发生这种情况,所以我最终还是使用了 grep 和 PCRE。我目前的解决方案是

regex="\[[^\]]*?\]"
if [[ $(grep -o '\[.*\]' <<< $STR) ]];
then
    MATCHES=$(grep -oP "$regex" <<< $STR)
else
    echo "No special flags provided."
    exit 0
fi

然后我继续进行 for 循环:

for arg in $MATCHES;
do
    echo $arg
done

问题是它没有像我希望的那样分隔字段。我使用 hexdump 来找出正确的分隔符:

hexdump -C <<< $MATCHES

令我惊讶的是,它表明分隔符是十六进制的0aLF。这不是问题,因为我知道 for 循环使用 IFS 进行分割。然后我通过使用将 IFS 设置为 LF IFS=$'\n'。令我(再次)惊讶的是,0a0a根据 hexdump 再次将 IFS 的值设置为 。所以那不起作用。然后,我将 IFS 的值设置为IFS='',(这是我的第三个惊喜)将该值设置为0a。但这也不起作用,for 循环没有改变行为。也许我的脚本没有正确设置 IFS 的范围?

我的问题如下:

1)为什么原来的 bash only regex 方法不起作用?为什么它只捕获一个字符? regex101 dot com 显示了预期的行为,但话又说回来,它不提供 bash 正则表达式模式。

2)为什么IFS集没有像我预期的那样工作?它添加了一个“额外”的 LF,即使我将其设置为空。

3)为什么IFS似乎不影响for循环?

4)有没有一种更简单的方法可以让我解决原来的问题([foo] [bar] [foo bar]从像 这样的字符串中提取[foo] [bar] 1 asdf[foo bar],以一种可以循环每个括号对的方式)。


奖金问题!

B) 我很困惑何时应该用引号或双引号将变量或表达式括起来。我已经阅读了一些有关通配符和参数扩展的内容,现在正在寻找更深入的内容。有什么建议吗?

答案1

要匹配任何不包含 的非空字符串],请使用[^]]+

使用[^\]]*将匹配非\后跟零个或多个]。这就是为什么您设法解析出 the1和 the2而不是其他字符串。

IFS变量不会在您的第一段代码中发挥作用。里面的变量[[ ... ]]不需要双引号。

要打印数组的单独元素,请使用

printf '%s\n' "${MATCHES[@]}"

或者

for elem in "${MATCHES[@]}"; do
    printf '%s\n' "$elem"
done

只是$MATCHES会扩展到数组的第一个元素(并对值应用分词和文件名通配)。

相关内容