Tracks
/
jq
jq
/
Lehrplan
/
Grundlagen
Gr

Grundlagen in jq

1 Übung

Über Grundlagen

jq funktioniert so, dass die eingehenden JSON-Daten durch einen einzigen Ausdruck (geschrieben als Pipeline von Filtern) geleitet werden, um die gewünschten umgewandelten Daten zu erhalten.

Eine kurze Einführung in die Kommandozeilenoptionen von jq

Die Sprache jq wird durch das jq-Programm implementiert. Dieses Programm bietet mehrere praktische Kommandozeilenoptionen, mit denen du steuerst, wie die Eingabe verarbeitet und die Ausgabe dargestellt wird.

In den folgenden Beispielen lernst du kennen:

  • -n oder --null-input

    Normalerweise bekommt das jq-Programm eine Datei zum Lesen, oder du schickst Daten an seine Eingabe. Die Option --null-input erlaubt es dir, JSON-Daten ohne jede Eingabe zu erzeugen.

  • -c oder --compact-output

    Standardmäßig gibt jq seine Ausgabe übersichtlich formatiert aus. Es ist für Menschen äußerst nützlich, die Daten anzusehen, wenn sie schön formatiert sind. Für Maschinen ist das jedoch nicht nötig: Die Option --compact-output entfernt den Formatierungsleerraum, um die Größe des resultierenden JSON zu minimieren.

  • -f filename oder --from-file filename

    Lies das jq-Programm aus filename, statt es auf der Kommandozeile anzugeben. sed und awk verwenden beide die Option -f für denselben Zweck. Du wirst sehen, dass dies in den Testskripten für die praktischen Übungen verwendet wird.

Siehe das Handbuch für Details zu allen Optionen.

Filter und Pipes

Filter werden auch als Ausdrücke bezeichnet.

Ein Filter nimmt eine Eingabe und erzeugt eine Ausgabe. Wie in einer Unix-Shell kannst du Filter mit einer Pipe | verketten, um die Ausgabe eines Filters mit der Eingabe eines anderen zu verbinden.

Der Identitätsfilter: .

Das ist der einfachste Filter. Er gibt seine Eingabe einfach an seine Ausgabe weiter. Zum Beispiel gibt jq standardmäßig formatiert aus. Schickst du JSON durch den .-Filter, bekommst du also gratis eine schön formatierte Ausgabe!

$ echo '[1, 2, 3]' | jq '.'
[
  1,
  2,
  3
]

Arrays

Dies ist eine kurze Einführung in die Arbeit mit Arrays. Wir werden dieses Thema später ausführlicher behandeln.

Auf die Elemente eines Arrays greifst du mit eckigen Klammern zu; sie sind nullbasiert indiziert.

$ echo '[10, 20, 30]' | jq '.[1]'
20

Ein Filter kann ein Array aufbauen, indem er einen Ausdruck in [ und ] einschließt:

  • mit einer bekannten Liste von Elementen:

    jq -n '[1, 2, 3]'
    
  • um einen Stream von Elementen zu sammeln: range zum Beispiel ist eine Funktion, die einen Stream von Zahlen ausgibt

    $ jq -n 'range(10; 70; 15)'
    10
    25
    40
    55
    

    Mit [] sammelst du die Ergebnisse des Ausdrucks in einem Array

    $ jq -c -n '[range(10; 70; 15)]'
    [10,25,40,55]
    

Das Komma ist ein Operator

Das Komma ist nicht nur Syntax, die Array-Elemente trennt. Das Komma ist ein Operator, der Streams verbindet.

Zum Beispiel ist [1, 2, 3] ein Filter, der den Array-Konstruktor [] verwendet, um das Ergebnis des Zusammenfügens der drei Ausdrücke 1, 2 und 3 zu sammeln.

Hast du die Semikolons in range(10; 70; 15) oben bemerkt? Weil Kommas in der Sprache jq einen bestimmten Zweck haben, trennen Funktionen mit mehreren Argumenten ihre Argumente mit Semikolons.

Objekte

Eine kurze Einführung in Objekte.

Ähnlich wie in vielen Programmiersprachen greifst du mit Punkten auf die Eigenschaften eines Objekts zu

$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"

Für Objekte kannst du auch eckige Klammern verwenden, aber dann brauchst du Anführungszeichen für String-Literale. Das ist eine Methode, um mit Schlüsseln zu arbeiten, die Leerzeichen enthalten.

$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"

Du kannst ein Objekt mit {} und key: value-Paaren konstruieren. Um Schlüssel, die „einfache“ Strings sind, sind keine Anführungszeichen nötig.

jq -n '{question: (6 * 9), answer: 42}'

liefert

{
  "question": 54,
  "answer": 42
}

Um den Schlüssel als Ausdruck zu behandeln, musst du ihn in runde Klammern setzen (das Folgende gibt ebenfalls dasselbe aus wie oben).

echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
Note

Es ist ganz üblich, eine Teilmenge von Schlüsseln aus einem großen Objekt extrahieren zu wollen. Um zum Beispiel id und name zu extrahieren aus

{
    "id": 101,
    "name": "alpha widget",
    "specifications": {...}
}

Wir könnten schreiben

{id: .id, name: .name}

Aber das ist so üblich, dass es eine Kurzschreibweise dafür gibt:

{id, name}

Pipelines

Zum Beispiel: Gegeben sei file.json mit dem Inhalt

{
  "key1": "value1",
  "key2": [5, 15, 25]
}

Berechnen wir die Länge des Arrays key2:

$ jq '.key2 | length' file.json
3

Wir leiten die Ausgabe des Ausdrucks .key2 als Eingabe an length weiter, das wenig überraschend die Anzahl der Elemente im Array ausgibt.

Caution

Das ist ein Aspekt von jq, an den man sich erst gewöhnen muss: Die meisten (aber nicht alle) Funktionen verhalten sich wie Filter, bei denen du die Daten an die Eingabe des Filters übergibst, nicht als Argument.

Filter können ihre Eingabe ignorieren

In diesem Beispiel werden die eingehenden JSON-Daten ignoriert und haben keinen Einfluss auf die Ausgabe:

$ echo '{"answer": 42}' | jq '6 * 9'
54

Filter können Streams von Daten ausgeben

Ein Filter kann mehr als einen Wert ausgeben. Zum Beispiel gibt der Filter .[] jedes Element eines Arrays als eigenen Wert aus:

$ jq -n -c '[1, 2, 3]'
[1,2,3]

$ jq -n -c '[1, 2, 3] | .[]'
1
2
3

Leitest du einen solchen Filter in einen anderen, wird der zweite Filter für jeden Wert ausgeführt:

$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6

Das ähnelt einer impliziten Iteration. Sobald du diese Technik verstehst, erkennst du, dass sehr mächtige jq-Filter sehr knapp sein können.

Runde Klammern

Runde Klammern gruppieren Teilausdrücke und erzwingen so die Reihenfolge der Auswertung, genau wie in anderen Sprachen. In jq kann die Notwendigkeit dafür etwas überraschend wirken.

Zum Beispiel: Angenommen, wir wollen ein Array mit 2 Elementen konstruieren: die Quadratwurzel von 9 und e hoch 1. Die beiden einzelnen Ausdrücke sind 9 | sqrt und 1 | exp. Wir erwarten als Ausgabe das Array [3, 2.7...]

$ jq -n '[ 9|sqrt, 1|exp ]'
[
  20.085536923187668,
  2.718281828459045
]

Warum haben wir nicht bekommen, was wir erwartet haben? jq interpretiert das so:

[ ((9|sqrt), 1) | exp ]

jq baut einen Stream aus zwei Elementen (3 und 1), die beide an exp übergeben werden.

Wir müssen sicherstellen, dass exp nur eine Zahl als Eingabe bekommt. Mit anderen Worten: Wir müssen erzwingen, dass die Pipe vor dem Komma ausgewertet wird.

$ jq -n '[ 9|sqrt, (1|exp) ]'
[
  3,
  2.718281828459045
]

Typen

Aus dem Handbuch

jq unterstützt dieselben Datentypen wie JSON: Zahlen, Strings, boolesche Werte, Arrays, Objekte (die in der JSON-Sprache Hashes mit ausschließlich String-Schlüsseln sind) und „null“.

Mehr über diese lernst du in späteren Übungen.

Leerraum

Leerraum ist in jq nicht bedeutsam. Verwende Leerzeichen, Tabs und Zeilenumbrüche, wie es dir passt, um deinen Code zu formatieren. Uns sind keine bestehenden jq-Styleguides bekannt.

Unveränderliche Werte

Werte in jq sind unveränderlich. Filter, die einen Wert verändern, geben einen neuen Wert aus. Das bedeutet, dass jq keine globalen Variablen hat. Du wirst dich daran gewöhnen müssen, Zustand von einem Filter zum nächsten weiterzugeben.

„Truthiness“

Die Werte false und null gelten als falsch. Jeder andere Wert (einschließlich der Zahl null und des leeren Strings/Arrays/Objekts) ist wahr.

Funktionen und Operatoren

Ohne zu sehr ins Detail zu gehen (Funktionen sind Thema einer anderen Übung), hier ein paar nützliche eingebaute Funktionen:

  • length

    Bei einem Array als Eingabe gibt sie die Anzahl der Elemente im Array aus.

    $ jq -n '[10, 20, 30, 40] | length'
    4
    
  • +

    Dieser Operator macht je nach Typ seiner Operanden etwas anderes: Er addiert Zahlen, er verkettet Strings, er hängt Arrays aneinander, er führt Objekte zusammen.

    $ jq -c -n '
        3 + 4,
        "foo" + "bar",
        ["a", "b"] + ["c"],
        {"m": 10} + {"n": 20}
    '
    7
    "foobar"
    ["a","b","c"]
    {"m":10,"n":20}
    

    add ist eine Funktion, die ein Array nimmt und einen Wert zurückgibt, bei dem alle Elemente nach den Regeln von + zusammengezählt wurden. [1, 2, 3] | add gibt 6 aus.

  • map

    Bei einem Array als Eingabe und einem Filter als Argument gibt sie ein Array aus, in dem der Filter auf jedes Element angewendet wird

    $ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
    [2,4,6,8]
    
  • select

    Bei einer Eingabe und einem Filter als Argument:

    • wenn der auf das Argument angewendete Filter einen wahren Wert ergibt, gibt sie die Eingabe unverändert aus
    • andernfalls gibt sie nichts aus (nicht den Wert null, wirklich keine Ausgabe)

    Wähle zum Beispiel aus einigen Zahlen die durch 3 teilbaren aus

    $ jq -n 'range(10) | select(. % 3 == 0)'
    0
    3
    6
    9
    

    Denk daran, dass range einen Stream von Zahlen ausgibt. select wird für jede Zahl einmal aufgerufen. Nur die Zahlen, die den Ausdruck „bestehen“, werden ausgegeben.

    Oft musst du Elemente eines Arrays auswählen. Dafür gibt es ein paar Möglichkeiten.

    Wähle bei der Eingabe ["Anne", "Bob", "Cathy", "Dave"] die Namen mit Länge 4 aus.

    • verwende map und select zusammen

      map(select(length == 4))
      
    • zerlege das Array in Elemente, wende select auf diesen Stream an und sammle die Ergebnisse

      [ .[] | select(length == 4) ]
      

Kommentare

Kommentare beginnen mit dem Zeichen # und reichen bis zum Ende der Zeile.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab

Lerne Grundlagen