JSON-Array zum Bash-Variablen mit jq

15

Ich habe ein JSON-Array wie folgt:

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

Ich möchte dieses Array mit jq durchlaufen, damit ich den Schlüssel jedes Elements als Variablennamen und den Wert als Wert festlegen kann.

Beispiel:

  • URL = "example.com"
  • AUTOR = "John Doe"
  • CREATED = "22.10.2017"

Was ich bisher habe, iteriert über das Array, erstellt aber einen String:

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

Welche Ausgänge:

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

Ich möchte diese Variablen weiter unten im Skript verwenden:

echo ${URL}

Dies spiegelt jedoch im Moment eine leere Ausgabe wider. Ich schätze, ich brauche ein evaloder etwas drin, kann aber nicht meinen Finger darauf legen.

EHerman
quelle

Antworten:

26

Ihre ursprüngliche Version wird nicht in der evalLage sein, da der Autorenname Leerzeichen enthält. Dies wird so interpretiert, dass ein Befehl Doemit der Umgebungsvariablen AUTHORauf ausgeführt wird John. Es ist auch praktisch nie erforderlich, jqsich selbst zu leiten - die interne Rohrleitung und der Datenfluss können verschiedene Filter miteinander verbinden.

Sie können eine viel einfachere Version des jq-Programms erstellen:

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

welche Ausgänge:

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

Es ist nicht erforderlich, dass a map: .[]jedes Objekt im Array als separates Element durch den Rest der Pipeline führt , sodass alles nach dem letzten Objekt separat auf jedes Objekt| angewendet wird. Am Ende stellen wir einfach eine gültige Shell-Zuweisungszeichenfolge mit normaler +Verkettung zusammen, einschließlich Anführungszeichen um den Wert.

Alle Pipes spielen hier eine Rolle - ohne sie erhalten Sie ziemlich wenig hilfreiche Fehlermeldungen, bei denen Teile des Programms in subtil unterschiedlichen Kontexten ausgewertet werden.

Diese Saite ist evalso lang wie die Charaktere in der Lage `, $, Newline und null nicht in den Daten angezeigt werden :

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

evalAchten Sie wie immer darauf, dass Sie den Daten vertrauen, die Sie erhalten, denn wenn sie böswillig sind oder nur in einem unerwarteten Format vorliegen, können Fehler auftreten.

Michael Homer
quelle
13

Aufbauend auf der Antwort von @Michael Homer können Sie eine potenziell unsichere Situation evalvollständig vermeiden, indem Sie die Daten in ein assoziatives Array einlesen .

Wenn sich Ihre JSON-Daten beispielsweise in einer Datei mit dem Namen befinden 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]}'"

Ausgabe:

$ ./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
quelle
1
Sie können die Zuweisung auch allein mit declare -- “$key=$value”und indirekt $AUTHORohne Array indirekt ausführen. Es ist immer noch sicherer als eval, obwohl Änderungen PATHoder etwas noch weniger möglich sind als diese Version.
Michael Homer
1
Ja, das Array isoliert die Variablen gut in einen Container Ihrer Wahl - keine Chance, versehentlich / böswillig mit wichtigen Umgebungsvariablen herumzuspielen. Sie können Ihre declare --Version sicherer machen, indem Sie $ key mit einer Liste zulässiger Variablennamen vergleichen.
Cas
1

Ich habe gerade festgestellt, dass ich die Ergebnisse durchlaufen und jede Iteration bewerten kann:

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

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

Erlaubt mir:

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

Ich mag den @ Michael-Vorschlag sehr. Manchmal können Sie wirklich nur einen Variablenwert extrahieren, um eine Aufgabe auf diesem bestimmten Server mit BASH auszuführen. Gewünschte Variablen sind also bekannt. Mit diesem Ansatz können Sie mehrere Aufrufe von jq vermeiden, um einen Wert pro Variable festzulegen, oder sogar die read-Anweisung mit mehreren Variablen verwenden, in denen einige gültig und leer sein können, was zu einer Wertverschiebung führt (das war mein Problem).

Mein vorheriger Ansatz, der dazu führt, führt zu einem Wertverschiebungsfehler, wenn .svID [] .ID = "" ( sv erhält den slotID- Wert

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

Wenn Sie das Objekt mit curl heruntergeladen haben, ist hier mein Ansatz, einige Variablen in einen Anzeigenamen umzubenennen, um Daten aus Datenarrays zu extrahieren

Die Verwendung von eval und filter löst das Problem mit einer Zeile und erzeugt Variablen mit dem gewünschten Namen

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

Der Vorteil in diesem Fall ist die Tatsache, dass im ersten Schritt alle gewünschten Variablen gefiltert, umbenannt und formatiert werden. Beachten Sie, dass dort drin ist. [0] | Dies ist sehr häufig der Fall, wenn die Quelle von einem RESTFULL-API-Server mit GET Antwortdaten wie folgt enthält:

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

Wenn Ihre Daten nicht aus einem Array stammen, z. ist ein Objekt wie:

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

Entfernen Sie einfach den Anfangsindex:

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

Dies ist eine alte Frage, aber ich fühlte mich geteilt, da es schwer zu finden war

Thiago Conrado
quelle