Eigentlich ist der Titel in diesem Fall falsch: Ich habe jq bisher nicht unterschätzt, sondern schlicht und einfach nicht gekannt. Das war eine echte Lücke in meinem Werkzeugkasten, weswegen ich es hier anpreise.

Für XML gibt es auf der Kommandozeile xmllint, dass eine Menge Sachen für XML kann (deutlich mehr als Syntaxcheck und hübsch ausgeben). Nun hat sich XML nicht in dem Maße durchgesetzt wie man es sich vor fünfzehn Jahren vorgestellt hat. Ein Grund dafür ist mit Sicherheit JSON als strukuturiertes Format, das mittlerweile eine wirklich große Verbreitung und Unterstützung in vielen Werkzeugen gefunden hat, sei es im Datenbankumfeld wie zum Beispiel in MongoDB, in PostgreSQL und ElasticSearch, als API für z.B. github oder auch einfach nur als Ausgabe und Konfigurationsformat kleinerer Programme. Die Unterstützung in Skriptsprachen ist hervorragend, auch in Go gehört encoding/json zur Standardbibliothek. Außerdem sind JSON-Datein deutlich kompakter als XML-Dateien. Wenn man schon mal Hashes in Perl, Pyton oder Javascript gesehen hat, erschließt sich eine JSON-Datei sofort – sie sind meistens gut lesbar. Es gibt also gute Gründe für JSON und dementsprechend viele Fälle, in denen man über json stolpert.

jq beschreibt sich als „a lightweight and flexible command-line JSON processor“ und ist ein relativ handliches C-Programm ohne besondere Laufzeitabhängigkeiten. Der einfachste Aufruf ist

$ echo '{"vikings": true, "food": ["spam", "sausages", "spam", "eggs", "spam"]}' | jq '.'
{
  "vikings": true,
  "food": [
    "spam",
    "sausages",
    "spam",
    "eggs",
    "spam"
  ]
}

Das ist eine einfache und unspektakuläre lesbare Ausgabe des Eingabedokumentes, was ja schon mal nett ist. Der '.' als Filter reicht die Eingabe einfach durch. Etwas spannender wird es, wenn man nur die Speisen sehen möchte. Dazu macht man

$ echo '{"vikings": true, "food": ["spam", "sausages", "spam", "eggs", "spam"]}' | jq '.food'
[
  "spam",
  "sausages",
  "spam",
  "eggs",
  "spam"
]

Damit wird nur das Element food ausgewählt. Nächster Schritt: Reduzierung des Spam-Überangebotes:

$ echo '{"vikings": true, "food": ["spam", "sausages", "spam", "eggs", "spam"]}' | jq '.food|unique'
[
  "eggs",
  "sausages",
  "spam"
]

Das zeigt schon ein wenig der Möglichkeiten, die im sehr lesenswerten Tutorial vorgestellt und im Manual gründlich beschrieben werden.

Trotzdem wirkt das noch etwas synthetisch – spannend wird es, wenn man sich das JSON zum Beispiel mit curl von Elasticsearch holt. Elasticsearch ist ein wirklich cooles Produkt, in das man wirklich große Datenmengen einfüllen und wieder rausholen kann. Das API von Elasticsearch ist ein REST-Api mit json, in der Dokumentation wird zum Aufrufen immer curl verwendet. Die Ausgabe kann allerdings sehr lang werden, und da kann jq dann wirklich nützen. Ein einfaches Beispiel:

$ curl -s -XGET 'http://localhost:9200/_cluster/health?pretty'
{
  "cluster_name" : "elasticsearch",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 5,
  "active_shards" : 5,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 5,
  "number_of_pending_tasks" : 0
}

Das ?pretty sorgt dafür, dass Elasticsearch das JSON lesbarer macht. Ohne es wäre die Ausgabe eine einzelne lange Zeile, aber die könnte man durch jq pipen. Wenn einen hier nur der Wert von status interessiert, kann man das einfach mit grep erledigen, oder man macht

$ curl -s -XGET 'http://localhost:9200/_cluster/health' | jq '.["status"]'
"yellow"

(.["status"] ist eine umständliche Art, .status zu schreiben – das ich das hier benutze liegt eher am Spieltrieb).

Will man sich aber wie in der Dokumentation zu den Index-Statistiken beschrieben die Statistiken zu den Indizes anzeigen lassen, hilft grep nicht mehr weiter, weil die Angaben für jeden Index wiederholt werden. Da hilft dann eine Abfrage wie die folgende:

$ curl -s -XGET 'http://localhost:9200/_stats' | jq '.indices.jqtest.primaries.segments'
{
  "count": 0,
  "memory_in_bytes": 0,
  "index_writer_memory_in_bytes": 0,
  "index_writer_max_memory_in_bytes": 335544320,
  "version_map_memory_in_bytes": 0,
  "fixed_bit_set_memory_in_bytes": 0
}

Das ist nur ein Anriss der Möglichkeiten, aber das macht trotzdem klar, dass es sich lohnt, sich in jq einzuarbeiten, wenn man mit JSON-Dateien zu tun hat.