array JSON para bash variáveis usando jq

6

Eu tenho um array JSON assim:

{
  "SITE_DATA": {
    "URL": "example.com",
    "AUTHOR": "John Doe",
    "CREATED": "10/22/2017"
  }
}

Estou procurando iterar sobre esse array usando jq para poder definir a chave de cada item como o nome da variável e o valor como valor.

Exemplo:

  • URL="example.com"
  • AUTHOR="João da Silva"
  • CREATED="22/10/2017"

O que eu tenho até agora se repete na matriz, mas cria uma string:

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

Quais resultados:

URL=example.com
AUTHOR=John Doe
CREATED=10/22/2017

Estou procurando usar essas variáveis mais abaixo no script:

echo ${URL}

Mas isso ecoa uma saída vazia no momento. Eu estou supondo que eu preciso de um eval ou algo assim, mas não consigo colocar meu dedo sobre isso.

    
por EHerman 30.12.2017 / 23:50

3 respostas

17

A sua versão original não será eval able porque o nome do autor tem espaços - seria interpretado como executando um comando Doe com a variável de ambiente AUTHOR definida como John . Também não há praticamente necessidade de canalizar jq para si mesmo - o encanamento interno & O fluxo de dados pode conectar diferentes filtros juntos.

Você pode fazer uma versão muito mais simples do programa jq:

jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""'

quais saídas:

URL="example.com"
AUTHOR="John Doe"
CREATED="10/22/2017"

Não há necessidade de map : .[] lida com cada uma delas objeto na matriz através do restante do pipeline como um item separado , então tudo após o último | é aplicado a cada um separadamente. No final, apenas montamos uma string de atribuição de shell válida com concatenação + comum, incluindo aspas em torno do valor.

Todos os canais são importantes aqui - sem eles, você recebe mensagens de erro bastante inúteis, em que partes do programa são avaliadas em contextos sutilmente diferentes.

Esta string é eval desde que os caracteres ' , $ , newline e null não apareçam nos dados:

eval "$(jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""' < data.json)"
echo "$AUTHOR"

Como sempre, ao usar eval , tome cuidado para confiar nos dados que você recebe, pois, se for malicioso ou apenas em um formato inesperado, as coisas podem dar errado.

    
por 31.12.2017 / 00:10
7

Com base na resposta do @Michael Homer, você pode evitar um eval potencialmente inseguro lendo os dados em um array associativo.

Por exemplo, se seus dados JSON estiverem em um arquivo chamado file.json :

#!/bin/bash

typeset -A myarray

while IFS== read -r key value; do
    myarray["$key"]="$value"
done < <(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + .value ' file.json)

# show the array definition
typeset -p myarray

# make use of the array variables
echo "URL = '${myarray[URL]}'"
echo "CREATED = '${myarray[CREATED]}'"
echo "AUTHOR = '${myarray[URL]}'"

Saída:

$ ./read-into-array.sh 
declare -A myarray=([CREATED]="10/22/2017" [AUTHOR]="John Doe" [URL]="example.com" )
URL = 'example.com'
CREATED = '10/22/2017'
AUTHOR = 'example.com'
    
por 31.12.2017 / 03:36
1

Acabei de perceber que posso repetir os resultados e avaliar cada iteração:

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

for key in ${constants}; do
  eval ${key}
done

me permite fazer:

echo ${AUTHOR}
# outputs John Doe
    
por 30.12.2017 / 23:54

Tags