Como ler um arquivo de propriedades em um array associativo?

4

Gostaria de ler as propriedades em um arquivo de propriedades em um array associativo. Como posso fazer isso?

Específicos sobre o que analisar: hash e sinais de igual. Tudo o mais é um bônus.

Exemplo de conteúdo do arquivo de propriedades:

# comment
a=value-a
b=http://prefix.suffix:8080/?key=value
c=password_with\backslash-and=equals

Gostaria que esse array associativo do bash fosse construído a partir desse arquivo:

 declare -A props='(
  [a]="value-a"
  [b]="http://prefix.suffix:8080/?key=value"
  [c]="password_with\backslash-and=equals" )'

(saída esperada de declare -p nesse array associativo, observe que ${props[c]} contém apenas uma barra invertida, "\" é '\' ).

    
por AlikElzin-kilaka 18.07.2016 / 17:18

3 respostas

5

Use um analisador parecido como perl 's Config::Properties módulo. Eu faria o script inteiro em perl , mas se você tivesse que usar bash , você poderia fazer algo como:

typeset -A props
while IFS= read -rd '' key && IFS= read -rd '' value; do
  props[$key]=$value
done < <(
  perl -MConfig::Properties -l0 -e '
   $p = Config::Properties->new();
   $p->load(STDIN);
   print for $p->properties' < file.properties
)

(também funciona com zsh ).

A implementação de um analisador completo em bash seria muito trabalhoso e significaria reinventar a roda. Você pode implementar um bom subconjunto com um loop while read simples, já que o read embutido espera um formato de entrada muito semelhante a esses arquivos de propriedades:

typeset -A props
while IFS=$':= \t' read key value; do
  [[ $key = [#!]* ]] || [[ $key = "" ]] || props[$key]=$value
done < file.properties

(também funciona com ksh93 e zsh , os outros dois shell parecidos com o Bourne suportando matrizes associativas).

Isso lida com:

  • prop = value
  • prop: value
  • prop value
  • comentários no início da linha ( ! e # com espaços em branco à esquerda opcionais)
  • escape de barra invertida (como em foo\:\:bar=value para chaves que contêm delimitadores ou foo=\ bar ou password_with\backslash-and=equals em sua amostra).
  • continuação de linha com barra invertida

No entanto, se verificarmos a especificação

  • Isso não processa \n , \r , \uXXXX ... sequências
  • O LF é o único delimitador de linha reconhecido (não CR nem CRLF).
  • O FF não é reconhecido como um espaço em branco (não podemos simplesmente adicioná-lo a $IFS , porque \f não é um caractere de espaço em branco IFS).
  • para uma entrada como foo: bar = , que armazena bar em ${props[foo]} em vez de bar = ( foo: bar:baz: está OK embora). Isso é apenas um problema quando o valor da propriedade contém um (sem escape) delimitador ( : opcionalmente cercado por caracteres SPC / TAB, = opcionalmente cercado por caracteres SPC / TAB ou seqüência de um ou mais caracteres SPC / TAB) e é no final.
  • trata como linhas de comentários que começam com \! ou \# . Somente um problema para propriedades cujo nome começa com ! ou # .
  • em

    prop=1\
     2\
     3
    

    obtemos 1 2 3 em vez de 123 : os espaços iniciais não são ignorados nas linhas de continuação como deveriam ser.

por 19.07.2016 / 08:19
3

Para o subconjunto mais comum desse formato de dados, você pode usar uma função curta, usando expansão da variável bash e correspondência de expressão regular.

Observação: isso espera que as linhas estejam em ^key = value$ format ou ^#.*$ e ^!.*$ para comentários. Adapte o código ou pré-processe seus dados, caso contrário

$ cat /tmp/propdata 
k1 = v1
# A comment
k2 = v2and some s=t=u=f=f
! Another comment
k3 = v3

$ unset DATA
$ declare -A DATA

$ props(){ while read line || [[ -n $line ]]; do
[[ "$line" =~ ^#|^! ]] && continue;
if [[ "${line% =*}" ]]; then DATA[${line% =*}]="${line#*= }" ; fi ;
done < $1 ; }

$ props /tmp/propdata

$ echo "${DATA[k3]}"
v3
$ echo "${DATA[k2]}"
v2and some s=t=u=f=f

Editar: atualizado para aparar os espaços em torno do "=" para chave e valor

Edit2: Filtra os comentários agora também.

    
por 18.07.2016 / 18:31
0
declare -A properties
function readPopertyFile
{
    while read line || [[ -n $line ]]; do
        key='echo $line | cut -s -d'=' -f1'
        if [ -n "$key" ]; then
            value='echo $line | cut -d'=' -f2-'
            properties["$key"]="$value"
        fi
    done < $1
}

Uso:

readPopertyFile "file.properties"

Irá ler as propriedades em uma variável de matriz associativa chamada properties .

* Funciona no bash. Não sei sobre outras conchas.

* Não lida com propriedades de várias linhas.

    
por 18.07.2016 / 17:18