我有一个这种形式的复杂字符串:
inp="key1 = what' ever the value key2 = the value Nb.2 key3= \"last value\""
我需要获取与其第一个值关联的第一个键。我想使用 bash 正则表达式来提取键、值以及字符串中剩余的内容:
rkeyval="[[:space:]]*([_[:alnum:]]*?)[[:space:]]*=[[:space:]]*((.*?)[[:space:]]+([_[:alnum:]]+?[[:space:]]*=[[:space:]]*.*))"
if [[ $inp =~ $rkeyval ]]; then
key=${BASH_REMATCH[1]}
val=${BASH_REMATCH[3]}
left=${BASH_REMATCH[4]}
for i in $(seq 0 $(( ${#BASH_REMATCH[*]}-1 ))); do
echo -e "$i: \"${BASH_REMATCH[$i]}\"";
done;
else
echo "no match"
fi
这是行不通的。在我的装有 Bash 4.4 的 Mac 上,没有匹配项:
no match
在我的 Red Hat Linux 上,我得到以下输出:
0: "key1 = what' ever the value key2 = the value Nb.2 key3= "last value""
1: "key1"
2: "what' ever the value key2 = the value Nb.2 key3= "last value""
3: "what' ever the value key2 = the value Nb.2 "
4: "key3= "last value""
我期望以下输出:
0: "key1 = what' ever the value key2 = the value Nb.2 key3= "last value""
1: "key1"
2: "what' ever the value key2 = the value Nb.2 key3= "last value""
3: "what' ever the value"
4: "key3= "last value""
换句话说,键是第二个匹配组,值是第三个匹配组。
这个表达式适用于在线PHP 正则表达式测试器。
我希望它可以在任何具有 Bash 更新版本的 Unix 机器上运行。
我不知道为什么这不起作用,以及为什么结果从一个平台到另一个平台不同,即使我的正则表达式尊重 Posix 约定(或者确实如此?)。我在这里做错了什么?
答案1
POSIX 没有*?
定义ERE,Bash 使用的,相反指定:
多个相邻重复符号(“+”、“*”、“?”和间隔)的行为会产生未定义的结果。
重击使用系统regcomp
/regexec
用于正则表达式匹配。 Apple 的 libc 可能没有实现您想要的行为*?
。
没有标准方法可以从贪婪中恢复非贪婪匹配语义,尽管在这种情况下至少其中一些是不必要的([_[:alnum:]]*?
例如第一个)。否则,您需要转换表达式以匹配其他内容或改变数据提前(也可能是之后)以获得效果。
答案2
星号已经是一个可选计数(因为它可以是零个字符)。无需添加 an ?
。
那么,如果每个括号都捕获一个键或一个值可以吗?:
s='[[:space:]]*' # spaces
n='[_[:alnum:]]+' # a valid name (limited by spaces)
e="${s}=${s}" # an equal sign (=).
rkeyval="${s}(${n})${e}([^=]*) (${n})${e}([^=]*) (${n})${e}(.*)"
# 1^^^^^ 2^^^^^^ 3^^^^^ 4^^^^^^ 5^^^^^ 6^^^
echo "$rkeyval"
这将捕获如下:
if [[ $inp =~ $rkeyval ]]; then
i=0
while ((i<${#BASH_REMATCH[@]})); do
printf '%s: "%s"\n' "$((i))" "${BASH_REMATCH[i++]}";
done
else
echo "no match"
fi
印刷:
0: "key1 = what' ever the value key2 = the value Nb.2 key3= "last value""
1: "key1"
2: "what' ever the value"
3: "key2"
4: "the value Nb.2 "
5: "key3"
6: ""last value""
你想要的值(如果我正确理解你的代码)可以近似为(编辑以获得完美匹配):
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[@]:2:3}"
left="${BASH_REMATCH[@]:5:2}"