analizar un campo de una matriz JSON en una matriz bash

analizar un campo de una matriz JSON en una matriz bash

Tengo una salida JSON que contiene una lista de objetos almacenados en una variable. (Puede que no esté expresando eso bien)

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

Necesito todos los valores de item2 en una matriz para ejecutar un script bash en ubuntu 14.04.1.

He encontrado varias formas de obtener el resultado completo en una matriz, pero no solo los elementos que necesito.

Respuesta1

Usando:

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

Si necesita una forma más reforzada:

readarray -td '' arr

para entradas con nuevas líneas u otros caracteres especiales, evitando la división de palabras.

Producción:

value2
value2_2

Controlar:

Procesa la sustitución >(command ...)o <(...)se reemplaza por un nombre de archivo temporal. Escribir o leer ese archivo hace que los bytes se canalicen al comando interno. A menudo se utiliza en combinación con la redirección de archivos: cmd1 2> >(cmd2). Verhttp://mywiki.wooledge.org/ProcessSubstitution http://mywiki.wooledge.org/BashFAQ/024

Respuesta2

En realidad, lo siguiente tiene errores:

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

Si tiene bash 4.4 o posterior, hay disponible la mejor opción del mundo:

# 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")'
)

...mientras que con bash 4.0, puede ser conciso a costa de la detección de fallas y el soporte literal de nueva línea:

# 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' )

...o compatibilidad con bash 3.x y detección de fallas, pero sin soporte de nueva línea:

# 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'
)

...o compatibilidad con bash 3.x y soporte de nueva línea, pero sin detección de fallas:

# 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")')

Respuesta3

Úselo jqpara producir una declaración de shell que evalúe:

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

Dado el documento JSON en su pregunta, la llamada jqproducirá la cadena

arr=( 'value2' 'value2_2' )

que luego es evaluado por su shell. La evaluación de esa cadena creará la matriz nombrada arrcon los dos elementos value2y value2_2:

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

El @shoperador jqse encarga de citar correctamente los datos del shell.

Alternativamente, saque la arr=( ... )parte de la jqexpresión:

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

Ahora, jqsolo genera la lista de elementos citados, que luego se inserta arr=( ... )y se evalúa.

Si necesita leer datos de un curlcomando, utilícelo curl ... | jq -r ...en lugar de jq -r ... file.jsonen los comandos anteriores.

Respuesta4

Gracias a sputnick llegué a esto:

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

El JSON que tengo es el resultado de una API. Todo lo que necesitaba hacer era eliminar el argumento del archivo y canalizar |la salida de curl a jq. Funciona muy bien y ahorró algunos pasos.

información relacionada