bash正規表現抽出キー=値

bash正規表現抽出キー=値

次の形式の複雑な文字列があります。

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""

すなわち、キーは第2のマッチンググループであり、値は第3のマッチンググループである。

この表現はオンラインに適用されます。PHP正規表現テスター

最新バージョンのBashがインストールされているすべてのUnixシステムで動作したいと思います。

私の正規表現がPosixルールを尊重していますが(それはそうですか?)これがうまくいかないのか、結果がプラットフォームごとに異なるのかはわかりません。私がここで何を間違っているのか?

答え1

*?POSIXが定義されていません。むしろ、Bashで使用、代わりに指定:

複数の隣接する反復記号(「+」​​、「*」、「?」、および空白)の動作は、未定義の結果を生成します。

吹くシステム利用regcomp/regexec正規表現の一致に使用されます。 Apple の libc は、目的の動作を実装できない場合があります*?

Greedyでnon-greedy一致セマンティクスを回復する標準的な方法はありませんが、この場合、少なくとも一部は必要ありません([_[:alnum:]]*?最初のものと同じです)。それ以外の場合は、他のものと一致するように式を変換または変更する必要があります。データ効果を得る前(そしておそらく後)。

答え2

アスタリスクはすでにオプションの数です(0文字である可能性があるため)。 ?.

それでは、各括弧がキーや値をキャプチャしても大丈夫でしょうか? :

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}"

関連情報