analisar um campo de uma matriz JSON em uma matriz bash

analisar um campo de uma matriz JSON em uma matriz bash

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

Usando:

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 jqpara 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 jqproduzirá a string

arr=( 'value2' 'value2_2' )

que é então avaliado pelo seu shell. Avaliar essa string criará o array nomeado arrcom os dois elementos value2e value2_2:

$ eval "$( jq -r '@sh "arr=( \([.[].item2]) )"' file.json )"
$ printf '"%s"\n' "${arr[@]}"
"value2"
"value2_2"

O @shoperador in jqtem o cuidado de citar corretamente os dados do shell.

Alternativamente, mova a arr=( ... )parte para fora da jqexpressão:

eval "arr=( $( jq -r '@sh "\([.[].item2])"' file.json ) )"

Agora, jqgera apenas a lista de elementos citada, que é então inserida arr=( ... )e avaliada.

Se você precisar ler dados de um curlcomando, use curl ... | jq -r ...no lugar jq -r ... file.jsondos 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.

informação relacionada