bash regex extrair chave = valor

1

Eu tenho uma string complexa desta forma:

inp="key1 =   what' ever the value key2 = the value Nb.2   key3= \"last value\""

Eu preciso obter a primeira chave associada ao primeiro valor. Eu quero usar bash regex para extrair a chave, o valor e o que resta da string:

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

Isso não funciona. No meu Mac com Bash 4.4, não há correspondência:

no match

No meu Red Hat Linux, recebo a seguinte saída:

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

Espero a seguinte saída:

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

Em outras palavras, a chave seria o segundo grupo correspondente e o valor o terceiro.

Essa expressão funciona em um testador de expressões regulares do PHP .

Eu quero que isso funcione em qualquer máquina Unix que tenha uma versão atualizada do Bash.

Eu não sei porque isso não funciona, e porque o resultado é diferente de uma plataforma para outra, mesmo que o meu regex respeite a convenção Posix (ou não?). O que estou fazendo errado aqui?

    
por kaligne 09.01.2017 / 19:49

2 respostas

1

Um asterisco já é uma contagem opcional (como poderia ser zero caracteres). Não há necessidade de adicionar um ? a ele.

Então, tudo ficará bem se cada parêntese capturar uma chave ou um valor?:

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"

Isso vai capturar assim:

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

Impressão:

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

E os valores desejados (se eu entender seu código corretamente) podem ser aproximados por (editar para obter uma correspondência perfeita):

key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[@]:2:3}"
left="${BASH_REMATCH[@]:5:2}"
    
por 10.01.2017 / 21:17
2

POSIX não define *? para EREs , que o Bash usa, < href="http: //% 20http: //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04_06"> especificando em vez disso :

The behavior of multiple adjacent duplication symbols ( '+', '*', '?', and intervals) produces undefined results.

Bash usa o sistema regcomp / regexec para correspondência de expressão regular. A libc da Apple presumivelmente não implementa o comportamento desejado por *? .

Não há uma maneira padrão de recuperar a semântica de correspondência não-voraz do ganancioso, embora nesse caso pelo menos alguns deles sejam desnecessários (o primeiro [_[:alnum:]]*? , por exemplo). Caso contrário, você precisa transformar a expressão para corresponder a outra coisa ou alterar os dados antecipadamente (e provavelmente depois) para obter o efeito.

    
por 09.01.2017 / 23:12