2015-01-16 4 views
5

Ich verwende jq zum Parsen einer JSON-Datei, Extrahieren jedes JSON-Array in einer Serie in ein Shell-Array.Bash: Iterieren über Mitglieder eines JSON-Array ausgewählt durch Index

Meine aktuellen Code sieht wie folgt aus:

for ((i = 0; i < ${#nvars[@]}; i++)); do 
    v1=($(cat $INPUT | jq '."config"[i]."var1"[]')) 
    echo $v1 
done 

Fehlermeldung:

error: i is not defined 

ich auch

v1=($(cat $INPUT | jq '."config"[i]."var1"[]')) 

mit

ersetzt
v1=($(cat $INPUT | jq '."config"[$i]."var1"[]')) 

funktioniert immer noch nicht. Irgendeine Idee? Jede Hilfe wird geschätzt!


Edit: Beispieleingabedaten

{ 
    "config-vars":[ 
     { 
      "var1":["v1","v2"], 
      "var2":"" 
     }, 
     { 
      "var1":["v3",""], 
      "var2":"v4" 
     } 
    ] 
} 
+0

Hier gibt es einige sehr detaillierte Antworten. Meine bevorzugte Lösung, obwohl ihre Relevanz vom Kontrollfluss Ihres Skripts abhängt: 'each_obj() { lokal cmd =" ​​$ 1 "json_arr =" $ 2 "len i; len = $ (jq 'Länge' <(Echo "$ json_arr")); für ((i = 0; i Shane

+1

@Shane, würde ich vorschlagen, dass ein bisschen zu invertieren:' json_arr = "$ 1"; shift ", und dann können Sie' "$ @" 'verwenden, um einen Befehl auszuführen, der aus allen anderen Argumenten besteht, ohne die Probleme in http://mywiki.wooledge.org/BashFAQ/050 durch Verwendung von' $ cmd' zu riskieren. –

Antwort

14

Es gibt ein bisschen Raum für Verbesserungen. Beginnen wir hier:

v1=($(cat $INPUT | jq '."config"[$i]."var1"[]')) 

... erste, die Sie nicht wirklich cat verwenden müssen; Es verlangsamt Ihre Leistung, weil es jq zwingt, von einer Pipe lieber als von Ihrer Eingabedatei direkt zu lesen. Nur jq <"$INPUT" auszuführen wäre robuster (oder, besser, <"$input", um die Verwendung von Großbuchstaben zu vermeiden, die durch Konvention für Shell-Built-Ins und Umgebungsvariablen reserviert sind).

Zweitens müssen Sie alle Variablenerweiterungen angeben, einschließlich der Erweiterung des Namens der Eingabedatei - andernfalls erhalten Sie Fehler, wenn Ihr Dateiname Leerzeichen enthält.

Drittens array=($(stuff)) die Ausgabe von stuff auf alle Zeichen in IFS und erweitert als eine Reihe von glob Ausdrücke teilt (so die Ergebnisse dieser Spaltung, wenn die Ausgabe enthält *.txt, und Sie laufen dieses Skript in einem Verzeichnis, das Textdateien enthält, erhalten Sie die Namen dieser Dateien in Ihrem Ergebnis-Array). Wenn Sie nur auf Zeilenumbrüche splitten, bedeutet dies, dass Sie Mehrwort-Strings korrekt syntaktisch analysieren können, und das Deaktivieren der Glob-Erweiterung ist erforderlich, bevor Sie diese Technik bei Vorhandensein von Glob-Zeichen zuverlässig verwenden können. Eine Möglichkeit dazu besteht darin, IFS=$'\n' zu setzen und set -h auszuführen, bevor dieser Befehl ausgeführt wird. Ein anderer besteht darin, die Ausgabe Ihres Befehls in eine while read-Schleife umzuleiten (siehe unten).Die String-Ersetzung in Code ist in jeder Sprache eine schlechte Übung - so liegt (Bobby Tables) jemand, der nur in der Lage sein soll, die in Ihren Prozess übergebenen Daten zu ändern, um Inhalte bereitzustellen, die verarbeitet werden als ausführbarer Code (wenn auch in diesem Fall als jq Skript, das weniger gefährlich ist als die Ausführung willkürlichen Codes in einer volleren Sprache; trotzdem können dadurch zusätzliche Daten zur Ausgabe hinzugefügt werden).

Als nächstes, wenn Sie jq auszusenden Newline getrennte Inhalte sind immer, brauchen Sie es nicht in einem Array überhaupt zu lesen: Sie können über den Inhalt iterieren, wie es aus jq und lesen Sie in der Shell geschrieben, und verhindert so die Shell aus, um Speicher zuzuweisen, dass der Inhalt zu puffern:

while IFS= read -r; do 
    echo "read content from jq: $REPLY" 
done < <(jq -r --arg i "$i" '.config[$i | tonumber].var1[]' <"$input") 

Endlich - sagen wir, Sie tun wollen mit einem Array arbeiten. Es gibt zwei Möglichkeiten, um Fehler zu vermeiden. Eine davon ist IFS explizit zu setzen und glob Expansion vor der Zuweisung deaktivieren:

IFS=$'\n' # split only on newlines 
set -f 
result=($(jq -r ... <"$input")) 

Die andere mit einer Schleife zu Ihrem Array zuzuweisen ist:

result=() 
while IFS= read -r; do 
    result+=("$REPLY") 
done < <(jq -r ... <"$input") 

... oder, wie von @JohnKugelman vorgeschlagen zu verwenden read -a das gesamte Array in einem Arbeitsgang zu lesen:

IFS=$'\n' read -r -d '' -a result < <(jq -r ... <"$input") 
+0

Hallo @CharlesDuffy, ich habe versucht 'nvars = ($ (jq --arg i" $ i "'." Config-vars "[$ i]." Var1 "[]'" $ INPUT "))' aber Fehler bekommen : 'jq: error: Array kann nicht mit String indiziert werden. Mache ich etwas falsch? Vielen Dank! – odieatla

+0

@odieatla, was ist der Wert von 'i', den du passierst? –

+0

@odieatla, ... auch wäre es sehr hilfreich, wenn Ihre Frage Beispieldaten im selben Format enthält, mit dem Ihre Abfrage arbeiten muss. –

3

Variablen werden nicht in einfache Anführungszeichen interpoliert. Verwenden Sie stattdessen Anführungszeichen und entfernen Sie die vorhandenen Anführungszeichen.

v1=($(cat $INPUT | jq ".config[$i].var1[]")) 

Oder verwenden Sie die --arg Option und dann können Sie mit einfachen Anführungszeichen bleiben.

v1=($(cat $INPUT | jq --arg i "$i" '.config[$i].var1[]')) 

Sie könnten auch den nutzlosen Einsatz von Katze beheben:

v1=($(jq ".config[$i].var1[]" "$INPUT")) 

Auch sehen @ CharlesDuffy Antwort für eine große, detaillierte Erklärung, warum Array zuweisen wie dies nicht sicher ist.

+0

Danke für Ihre Hilfe. Noch eine Frage, was soll ich machen, wenn ich 'config-vars' anstelle von 'config' habe? Ich nehme an, das ist der Grund, warum ein Zitat verwendet wurde. Vielen Dank! – odieatla

+1

Versuchen Sie: '. [\" Config-vars \ "] [$ i]'. –

+0

Dies ist kein schlechter Ansatz, aber 'jq' kann als hilfreicher bezeichnet werden - es hat einen Modus, in dem die Ausgabe durch Zeilentrennzeichen getrennt ist und eine robustere Operation ermöglicht als das Aufteilen auf ein IFS-Zeichen. –

1

jq der Lage ist, die Struktur in einem Arbeitsgang der Extraktion, Die gesamte Schleife ist also überflüssig. Wenn der Eingabe-JSON mehr Datensätze enthält als Werte in nvars, verwenden Sie den Index zum Hacken.

jq -r '."config-vars"[]."var1"' "$INPUT" | 
head -n "${#nvars[@]}" # If you need just the #nvars first values 
1

Wenn Sie bereits das Ergebnis einer JSON in eine Variable gespeichert haben genannt $ MY_VAR:

while IFS= read -r; do 
    echo “$REPLY” 
done < <(echo $MY_VAR | jq -r ‘.[]‘) 

Es hat mich viel zu lange um dies herauszufinden. Alle Beispiele, die ich gesehen habe, waren verworren, und ich musste dies zusammenfügen.

Verwandte Themen