
Tenho uma saída JSON que contém uma lista de objetos armazenados em uma variável. (talvez eu não esteja dizendo isso direito)
[
{
"item1": "value1",
"item2": "value2",
"sub items": [
{
"subitem": "subvalue"
}
]
},
{
"item1": "value1_2",
"item2": "value2_2",
"sub items_2": [
{
"subitem_2": "subvalue_2"
}
]
}
]
Eu preciso de todos os valores do item2 em uma matriz para que um script bash seja executado no Ubuntu 14.04.1.
Eu encontrei várias maneiras de colocar o resultado inteiro em uma matriz, mas não apenas os itens que preciso
Responder1
Usandojq:
readarray arr < <(jq '.[].item2' json)
printf '%s\n' "${arr[@]}"
Se você precisar de uma forma mais rígida:
readarray -td '' arr
para entradas com novas linhas ou outros caracteres especiais, evitando a divisão de palavras.
Saída:
value2
value2_2
Verificar:
Substituição de processo >(command ...)
ou <(...)
é substituído por um nome de arquivo temporário. Escrever ou ler esse arquivo faz com que os bytes sejam canalizados para o comando interno. Frequentemente usado em combinação com redirecionamento de arquivo: cmd1 2> >(cmd2)
. Verhttp://mywiki.wooledge.org/ProcessSubstitution http://mywiki.wooledge.org/BashFAQ/024
Responder2
O seguinte é realmente problemático:
# BAD: Output line of * is replaced with list of local files; can't deal with whitespace
arr=( $( curl -k "$url" | jq -r '.[].item2' ) )
Se você possui o bash 4.4 ou mais recente, uma opção do melhor de todos os mundos está disponível:
# BEST: Supports bash 4.4+, with failure detection and newlines in data
{ readarray -t -d '' arr && wait "$!"; } < <(
set -o pipefail
curl --fail -k "$url" | jq -j '.[].item2 | (., "\u0000")'
)
... enquanto que com o bash 4.0, você pode ter concisão ao custo da detecção de falhas e suporte literal a nova linha:
# OK (with bash 4.0), but can't detect failure and doesn't support values with newlines
readarray -t arr < <(curl -k "$url" | jq -r '.[].item2' )
...ou compatibilidade com bash 3.x e detecção de falhas, mas sem suporte a nova linha:
# OK: Supports bash 3.x; no support for newlines in values, but can detect failures
IFS=$'\n' read -r -d '' -a arr < <(
set -o pipefail
curl --fail -k "$url" | jq -r '.[].item2' && printf '\0'
)
...ou compatibilidade com bash 3.x e suporte a nova linha, mas sem detecção de falhas:
# OK: Supports bash 3.x and supports newlines in values; does not detect failures
arr=( )
while IFS= read -r -d '' item; do
arr+=( "$item" )
done < <(curl --fail -k "$url" | jq -j '.[] | (.item2, "\u0000")')
Responder3
Use jq
para produzir uma instrução shell que você avalia:
eval "$( jq -r '@sh "arr=( \([.[].item2]) )"' file.json )"
Dado o documento JSON na sua pergunta, a chamada jq
produzirá a string
arr=( 'value2' 'value2_2' )
que é então avaliado pelo seu shell. Avaliar essa string criará o array nomeado arr
com os dois elementos value2
e value2_2
:
$ eval "$( jq -r '@sh "arr=( \([.[].item2]) )"' file.json )"
$ printf '"%s"\n' "${arr[@]}"
"value2"
"value2_2"
O @sh
operador in jq
tem o cuidado de citar corretamente os dados do shell.
Alternativamente, mova a arr=( ... )
parte para fora da jq
expressão:
eval "arr=( $( jq -r '@sh "\([.[].item2])"' file.json ) )"
Agora, jq
gera apenas a lista de elementos citada, que é então inserida arr=( ... )
e avaliada.
Se você precisar ler dados de um curl
comando, use curl ... | jq -r ...
no lugar jq -r ... file.json
dos comandos acima.
Responder4
Graças ao sputnick cheguei a isto:
arr=( $(curl -k https://localhost/api | jq -r '.[].item2') )
O JSON que tenho é a saída de uma API. Tudo o que eu precisava fazer era remover o argumento do arquivo e canalizar |
a saída do curl para jq. Funciona muito bem e salvou algumas etapas.