我有以下问题:我想从字符串中提取括号内的文本(带或不带括号)。我的字符串看起来像这样:
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
令我惊讶的是,它表明分隔符是十六进制的0a
LF。这不是问题,因为我知道 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
会扩展到数组的第一个元素(并对值应用分词和文件名通配)。