analisar um campo de uma matriz JSON na matriz bash

13

Eu tenho uma saída JSON que contém uma lista de objetos armazenados em uma variável. (Eu posso não estar falando 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.

Encontrei várias maneiras de obter todo o resultado em uma matriz, mas não apenas os itens de que preciso

JpaytonWPD
fonte

Respostas:

17

Usando :

$ cat json
[
  {
    "item1": "value1",
    "item2": "value2",
    "sub items": [
      {
        "subitem": "subvalue"
      }
    ]
  },
  {
    "item1": "value1_2",
    "item2": "value2_2",
    "sub items_2": [
      {
        "subitem_2": "subvalue_2"
      }
    ]
  }
]

CÓDIGO:

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

RESULTADO:

value2
value2_2
Gilles Quenot
fonte
É possível fazer isso de uma variável em vez de um arquivo? Tento evitar o excesso de acesso ao sistema de arquivos, se não precisar. Depois de puxar esse array, concluo a saída json.
precisa saber é o seguinte
1
Você já tentou alguma coisa?
Gilles Quenot
2
jq . <<< "$json"shell-lo de (bash) relacionado, não específica parajq
Gilles Quenot
1
Parênteses ausentes:arr=( $(...) )
Gilles Quenot
4
Ótimo jqcomando, mas por favor, não analise a saída do comando em uma matriz com arr=( $(...) )(mesmo que funcione com a entrada de amostra): ele não funciona como planejado com espaço em branco incorporado ou à esquerda / à direita e pode resultar em globbing acidental.
precisa
5

Na verdade, o seguinte é um bug:

# BAD: Output line of * is replaced with list of local files; can't deal with whitespace
arr=( $( curl -k "$url" | jq -r '.[].item2' ) )

Em vez disso, use:

# GOOD (with bash 4.x+), but can't detect failure
readarray -t arr < <(curl -k "$url" | jq -r '.[].item2' )

... ou melhor ainda ...

# GOOD (with bash 3.x+), *and* has nonzero status if curl or jq fails
IFS=$'\n' read -r -d '' -a arr \
  < <(set -o pipefail; curl --fail -k "$url" | jq -r '.[].item2' && printf '\0')
Charles Duffy
fonte
2

Graças ao sputnick, cheguei a isso:

arr=( $(curl -k https://localhost/api | jq -r '.[].item2') )

O JSON que eu tenho é a saída de uma API. Tudo o que eu precisava fazer foi remover o argumento do arquivo e canalizar |a saída de curl para jq. Funciona muito bem e salvou alguns passos.

JpaytonWPD
fonte
Esse código é realmente um pouco complicado. Veja o que acontece se você tiver um elemento de resultado *- ele será substituído por uma lista de arquivos em seu diretório atual.
Charles Duffy
1
Da mesma forma, um item2valor com espaço em branco se tornaria mais de um elemento da matriz.
Charles Duffy
0

como uma alternativa fácil, consulte a jtcferramenta (em https://github.com/ldn-softdev/jtc ) para obter a mesma coisa (como no exemplo do jq):

bash $ arr=( $(jtc -w '<item2>l+0' file.json) )
bash $ printf '%s\n' "${arr[@]}"
"value2"
"value2_2"
bash $ 

explicação sobre a -wopção: colchetes angulares <...>especificam a busca json inteira, o sufixo linstrui a procurar rótulos em vez de valores, +0instrui a encontrar todas as ocorrências (em vez de apenas a primeira).

Dmitry L.
fonte
O mesmo bug de todas as arr=( $(jq ...) )respostas, na medida em que o conteúdo está sendo dividido por cadeias e expandido para preencher a matriz - espaços de significado (não apenas novas linhas) criam novos elementos, e elementos que parecem uma expressão glob são substituídos por arquivos que expressão corresponde.
Charles Duffy