Matrice JSON per bash variabili usando jq


19

Ho un array JSON in questo modo:

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

Sto cercando di scorrere su questo array usando jq in modo da poter impostare la chiave di ogni elemento come nome della variabile e il valore come valore.

Esempio:

  • URL = "example.com"
  • AUTORE = "John Doe"
  • CREATO = "2017/10/22"

Quello che ho finora scorre sull'array ma crea una stringa:

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

Quali uscite:

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

Sto cercando di usare queste variabili più in basso nello script:

echo ${URL}

Ma questo echi un output vuoto al momento. Immagino di aver bisogno di evalqualcosa o qualcosa dentro ma non riesco a metterci il dito sopra.

Risposte:


29

La tua versione originale non sarà in evalgrado di farlo perché il nome dell'autore contiene spazi: verrebbe interpretato come in esecuzione di un comando Doecon la variabile di ambiente AUTHORimpostata su John. Inoltre, non è praticamente mai necessario collegarsi jqa se stesso: le tubazioni interne e il flusso di dati possono collegare diversi filtri insieme.

Puoi creare una versione molto più semplice del programma jq:

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

che produce:

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

Non c'è bisogno di map: si .[]occupa di portare ogni oggetto nella matrice attraverso il resto della pipeline come un elemento separato , quindi tutto ciò che segue l'ultimo |viene applicato a ciascuno separatamente. Alla fine, assembliamo solo una stringa di assegnazione shell valida con +concatenazione ordinaria , comprese le virgolette attorno al valore.

Tutte le pipe contano qui - senza di esse si ottengono messaggi di errore abbastanza inutili, in cui parti del programma vengono valutate in contesti leggermente diversi.

Questa stringa è evalin grado fino a quando i personaggi `, $, nuova riga e nulli non appaiono nei dati:

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

Come sempre durante l'utilizzo eval, fai attenzione a fidarti dei dati che stai ricevendo, poiché se sono dannosi o solo in un formato inaspettato le cose potrebbero andare molto male.


13

Basandoti sulla risposta di @Michael Homer, puoi evitare un tutto potenzialmente non sicuro evalleggendo i dati in un array associativo.

Ad esempio, se i tuoi dati JSON si trovano in un file chiamato 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]}'"

Produzione:

$ ./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'

1
Potresti anche indirizzare il compito da solo con declare -- “$key=$value”e far $AUTHORfunzionare ecc come nell'originale, senza un array. È ancora più sicuro di eval, anche se cambiare PATHo qualcosa è ancora possibile, quindi meno di questa versione.
Michael Homer,

1
Sì, l'array isola bene le variabili in un contenitore di tua scelta - nessuna possibilità di scherzare accidentalmente / maliziosamente con importanti variabili di ambiente. potresti rendere declare --sicura la tua versione confrontando $ key con un elenco di nomi di variabili consentiti.
Cas

1

Ho appena realizzato che posso ripetere i risultati e valutare ogni iterazione:

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

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

Mi permette di fare:

echo ${AUTHOR}
# outputs John Doe

0

Mi piace molto il suggerimento @Michel. A volte, puoi semplicemente estrarre il valore di alcune variabili per eseguire un'attività in quel server specifico usando BASH. Quindi, le variabili desiderate sono note. Questo uso di questo approccio è il modo per evitare o più chiamate a jq per impostare un valore per variabile o addirittura per utilizzare l'istruzione read con più variabili in cui alcune possono essere valide e vuote, portando a uno spostamento del valore (questo era il mio problema)

il mio approccio precedente che porta a un errore di spostamento del valore se .svID [] .ID = "" ( sv otterrà il valore slotID

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

Se hai scaricato l'oggetto usando curl, ecco il mio approccio per rinominare alcune variabili con un nome descrittivo come estrarre i dati dagli array di dati

l'utilizzo di eval e filtri risolverà il problema con una riga e produrrà variabili con il nome desiderato

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

Il vantaggio in questo caso è il fatto che filtrerà, rinominerà, formatterà tutte le variabili desiderate nel primo passaggio. Osserva che dentro c'è. [0] | che è molto comune avere se l'origine proviene da un server API RESTFULL che utilizza GET, dati di risposta come:

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

Se i tuoi dati non provengono da un array, ad es. è un oggetto come:

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

basta rimuovere l'indice iniziale:

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

Questa è una vecchia domanda, ma ho sentito la condivisione, poiché era difficile da trovare

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.