Matriz JSON para bash variables usando jq

19

Tengo una matriz JSON así:

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

Estoy buscando iterar sobre esta matriz usando jq para poder establecer la clave de cada elemento como el nombre de la variable y el valor como su valor.

Ejemplo:

  • URL = "ejemplo.com"
  • AUTOR = "John Doe"
  • CREADO = "22/10/2017"

Lo que tengo hasta ahora itera sobre la matriz pero crea una cadena:

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

Qué salidas:

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

Estoy buscando usar estas variables más abajo en el script:

echo ${URL}

Pero esto tiene una salida vacía en este momento. Supongo que necesito evalalgo o algo allí, pero parece que no puedo señalarlo.

EHerman
fuente

Respuestas:

29

Su versión original no evalpodrá porque el nombre del autor tiene espacios; se interpretará como ejecutar un comando Doecon la variable de entorno AUTHORestablecida en John. Prácticamente, nunca es necesario conectarlo jqa sí mismo: la tubería interna y el flujo de datos pueden conectar diferentes filtros entre sí.

Puede hacer una versión mucho más simple del programa jq:

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

que salidas:

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

No hay necesidad de un map: .[]trata de tomar cada objeto en la matriz a través del resto de la tubería como un elemento separado , por lo que todo después del último |se aplica a cada uno por separado. Al final, simplemente ensamblamos una cadena de asignación de shell válida con +concatenación ordinaria , incluyendo comillas alrededor del valor.

Todas las canalizaciones importan aquí: sin ellas, se obtienen mensajes de error bastante inútiles, donde se evalúan partes del programa en contextos sutilmente diferentes.

Esta cadena es evalcapaz, siempre y cuando los personajes `, $, nueva línea y nula no aparecen en los datos:

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

Como siempre al usar eval, tenga cuidado de confiar en los datos que está obteniendo, ya que si es malicioso o solo en un formato inesperado, las cosas podrían salir muy mal.

Michael Homer
fuente
13

Sobre la base de la respuesta de @Michael Homer, puede evitar una situación potencialmente insegura evalal leer los datos en una matriz asociativa.

Por ejemplo, si sus datos JSON están en un archivo llamado 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]}'"

Salida:

$ ./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'
cas
fuente
1
También puede indirecta la asignación solo con declare -- “$key=$value”y hacer que $AUTHORetc. funcione como en el original, sin una matriz. Todavía es más seguro que eval, aunque cambiar PATHo algo todavía es posible, por lo que es menos que esta versión.
Michael Homer
1
Sí, la matriz aísla muy bien las variables en un contenedor de su elección, sin posibilidad de jugar accidental / maliciosamente con variables de entorno importantes. puede hacer que su declare --versión sea segura comparando $ key con una lista de nombres de variables permitidos.
cas
1

Me acabo de dar cuenta de que puedo recorrer los resultados y evaluar cada iteración:

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

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

Me permite hacer:

echo ${AUTHOR}
# outputs John Doe
EHerman
fuente
0

Realmente me gusta la sugerencia de @Michel. A veces, realmente puede extraer el valor de algunas variables para ejecutar una tarea en ese servidor específico usando BASH. Entonces, las variables deseadas son conocidas. El uso de este enfoque es la forma de evitar o hacer múltiples llamadas a jq para establecer un valor por variable o incluso usar la declaración de lectura con múltiples variables en las que algunas pueden ser válidas y vacías, lo que lleva a un cambio de valor (ese fue mi problema)

mi enfoque anterior de que el plomo conducirá a un error de cambio de valor si .svID [] .ID = "" ( sv obtendrá el valor de slotID

-rd '\n' getInfo sv slotID <<< $(jq -r '(.infoCMD // "no info"), (.svID[].ID // "none"), (._id // "eeeeee")' <<< $data)

Si descargó el objeto usando curl, aquí está mi enfoque para cambiar el nombre de algunas variables a un nombre descriptivo como extraer datos de matrices de datos

El uso de eval y los filtros resolverá el problema con una línea y producirá variables con el nombre deseado.

eval "$(jq -r '.[0] | {varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

La ventaja en este caso es el hecho de que filtrará, cambiará el nombre y formateará todas las variables deseadas en el primer paso. Observe que allí hay. [0] | eso es muy común tener si la fuente es de un servidor API RESTFULL usando GET, datos de respuesta como:

[{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}]

Si sus datos no son de una matriz, es decir. es un objeto como:

{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}

simplemente elimine el índice inicial:

eval "$(jq -r '{varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

Esta es una vieja pregunta, pero sentí compartir, ya que era difícil de encontrar

Thiago Conrado
fuente