So analysieren Sie eine JSON-Datei über die Linux-Befehlszeile mit jq
Das JSON-Format (JavaScript Object Notation) wird häufig zur Darstellung von Datenstrukturen verwendet und häufig zum Datenaustausch zwischen verschiedenen Schichten einer Anwendung oder durch die Verwendung von API-Aufrufen verwendet. Wir wissen wahrscheinlich, wie man mit den am häufigsten verwendeten Programmiersprachen mit JSON-formatierten Daten interagiert, z. B. mit JSON und Python, aber was ist, wenn wir über die Befehlszeile oder in einem Bash-Skript damit interagieren müssen? In diesem Artikel werden wir sehen, wie wir eine solche Aufgabe mit dem Dienstprogramm jq erledigen können, und wir werden seine grundlegende Verwendung erlernen.
In diesem Tutorial lernen Sie:
- So installieren Sie jq in den am häufigsten verwendeten Linux-Distributionen oder kompilieren es aus dem Quellcode
- So verwenden Sie jq, um JSON-formatierte Daten zu analysieren
- So kombinieren Sie Filter mit "" und "|"
- So verwenden Sie die Funktionen length, keys, has und map
sudo apt install jqWenn wir die Red Hat-Distributionsfamilie bevorzugen, wie z.B. Fedora, CentOS oder RHEL, können wir jq über den dnf-Paketmanager installieren (in den letzten Versionen dieser Distributionen hat es yum abgelöst). Um das Paket zu installieren, würden wir Folgendes ausführen:
sudo dnf install jqDie Installation von jq auf Archlinux ist genauso einfach. Der Distributionspaket-Manager ist pacman, und das Paket ist im Community-Repository verfügbar. Wir können die Installation mit dem folgenden Befehl durchführen:
sudo pacman -S install jqWenn wir das nicht können oder aus irgendeinem Grund kein vorgefertigtes Binärpaket verwenden wollen, können wir jq aus dem Quellcode kompilieren. In
den folgenden Zeilen beschreiben wir die erforderlichen Schritte.
Erstellen und Installieren von der Quelle aus
Um jq aus dem Quellcode zu erstellen und zu installieren, müssen wir zunächst einen Release-Tarball herunterladen. Zum Zeitpunkt des Schreibens von
ist die neueste verfügbare Version 1.6. Um den Tarball herunterzuladen, ohne das Terminal zu verlassen, können wir wget verwenden:
wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gzSobald der Download abgeschlossen ist, müssen wir den Tarball dekomprimieren und extrahieren:
tar -xzf jq-1.6.tar.gzDer nächste Schritt besteht darin, das Verzeichnis jq-1.6 einzugeben, das als Ergebnis des letzten Befehls erstellt wurde:
cd jq-1.6Um den Quellcode zu kompilieren, benötigen wir nun die folgenden Dienstprogramme:
- gcc
- Automatisch erstellen
- lib-Werkzeug
- machen
Um die Software zu erstellen, führen wir Folgendes aus:
autoreconf -fi
./configure && make && sudo make installDer Befehl make install bewirkt standardmäßig, dass Binärdateien im Verzeichnis /usr/local/bin und Bibliotheken in /usr/local/lib installiert werden. Wenn wir die Installation anpassen und diese Verzeichnisse ändern möchten, müssen wir ein anderes Präfix angeben, indem wir die Option --prefix verwenden, wenn wir das ./configure-Skript starten.
Um die Software beispielsweise nur für einen bestimmten Benutzer zu installieren, könnten wir das Verzeichnis $HOME/.local als Präfix übergeben: In diesem Fall würden Binärdateien in $HOME/.local/bin und Bibliotheken in die $HOME/.local/lib installiert werden; Mit einer solchen Konfiguration wäre es nicht erforderlich, den Befehl make install mit Administratorrechten zu starten. Wenn Sie wissen möchten, wie Sie die aus der Quelle installierte Software besser organisieren können, können Sie unseren Artikel über das GNU-Dienstprogramm stow lesen.
Verwendung
Sobald wir jq installiert haben, können wir es verwenden, um JSON-Dateien von der Befehlszeile aus zu analysieren. Für dieses Tutorial werden wir mit einer einfachen Datenstruktur arbeiten, die einige Details zu drei Charakteren aus dem Herr der Ringe-Buch enthält. Die Daten werden in der characters.json Datei gespeichert.
Das Dienstprogramm jq wendet Filter auf einen Stream von JSON-Daten an. Als erstes verwenden wir den einfachsten Filter, ., der die Eingabedaten unverändert, aber hübsch gedruckt zurückgibt. Für diese Eigenschaft kann es verwendet werden, um Daten besser lesbar zu formatieren:
jq . characters.jsonDer obige Befehl erzeugt die folgende Ausgabe:
{
"characters": [
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
},
{
"name": "Legolas",
"race": "elf"
}
]
}
Nehmen wir nun an, wir möchten die Daten filtern, um nur den Wert zu erhalten, der dem Zeichenschlüssel zugeordnet ist. Um die Aufgabe auszuführen, geben wir den Namen des Schlüssels an und rufen seinen Wert ab (oder null, wenn er nicht vorhanden ist):
jq .characters characters.jsonIn unserem Beispiel ist der Wert, der mit dem Schlüssel "characters" verknüpft ist, ein Array, so dass wir das folgende Ergebnis erhalten:
[
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
},
{
"name": "Legolas",
"race": "elf"
}
]Was ist, wenn wir nur das erste Element des Arrays abrufen möchten? Wir müssen nur den richtigen Index daraus "extrahieren". Da wir wissen, dass Arrays nullbasiert sind, können wir Folgendes ausführen:
jq .characters[0] characters.jsonDer Befehl gibt uns:
{
"name": "Aragorn",
"race": "man"
}
Wir können auch einen Slice des Arrays erhalten. Angenommen, wir möchten nur die ersten beiden Elemente abrufen. Wir führen:
jq .characters[0:2] characters.jsonDer Befehl liefert uns folgendes Ergebnis:
[
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
}
]
Slicing funktioniert auch mit Strings, wenn wir also ausführen:
jq .characters[0].name[0:2] characters.jsonWir erhalten eine Scheibe (die ersten beiden Buchstaben) der "Aragorn"-Zeichenfolge: "Ar".
Separater Zugriff auf Array-Elemente
In den obigen Beispielen haben wir den Inhalt des Arrays "characters" gedruckt, das aus drei Objekten besteht, die Fantasy-Charaktere beschreiben. Was ist, wenn wir über dieses Array iterieren möchten? Wir müssen so machen, dass die darin enthaltenen Elemente separat zurückgegeben werden, also müssen wir [] verwenden, ohne einen Index anzugeben:
jq .characters[] characters.jsonDie Ausgabe des Befehls lautet:
{
"name": "Aragorn",
"race": "man"
}
{
"name": "Gimli",
"race": "dwarf",
"weapon": "axe"
}
{
"name": "Legolas",
"race": "elf"
}
In diesem Fall haben wir 3 Ergebnisse erhalten: die im Array enthaltenen Objekte. Die gleiche Technik kann verwendet werden, um über die Werte eines Objekts zu iterieren, in diesem Fall über den ersten, der im Array "characters" enthalten ist:
jq .characters[0][] characters.jsonHier erhalten wir folgendes Ergebnis:
"Aragorn"
"man"
Die Operatoren "," und "|"
Die Operatoren "," und "|" werden beide verwendet, um zwei oder mehr Filter zu kombinieren, funktionieren jedoch auf unterschiedliche Weise. Wenn zwei Filter durch ein Komma getrennt sind, werden sie beide getrennt auf die gegebenen Daten angewendet, so dass wir zwei unterschiedliche Ergebnisse erhalten. Sehen wir uns ein Beispiel an:
jq '.characters[0], .characters[2]' characters.jsonDie in der characters.json-Datei enthaltenen JSON-formatierten Daten werden zuerst mit .characters[0] und dann mit .charaters[2] gefiltert, um das erste und das dritte Element des Arrays "characters" abzurufen. Durch Ausführen des obigen Befehls erhalten wir zwei separate Ergebnisse:
{
"name": "Aragorn",
"race": "man"
}
{
"name": "Legolas",
"race": "elf"
}Der Operator "|" funktioniert anders, ähnlich wie eine Unix-Pipe. Die Ausgabe, die vom Filter links vom Operator erzeugt wird, wird als Eingabe an den Filter rechts vom Operator übergeben. Wenn ein Filter links neben dem Operator mehrere Ergebnisse erzeugt, wird der Filter rechts neben dem Operator auf jedes Ergebnis angewendet:
jq '.characters[] | .name' characters.jsonIn diesem Beispiel haben wir zwei Filter. Auf der linken Seite des Operators befindet sich der Filter .characters[], der, wie wir zuvor gesehen haben, es uns ermöglicht, die Elemente des Arrays "characters" als separate Ergebnisse zu erhalten. In unserem Fall ist jedes Ergebnis ein Objekt mit den Eigenschaften "name" und "race". Der .name-Filter rechts neben dem Operator | wird auf jedes der Objekte angewendet, sodass wir das folgende Ergebnis erhalten:
"Aragorn"
"Gimli"
"Legolas"Funktionen
Das jq-Dienstprogramm enthält einige sehr nützliche Funktionen, die wir auf die json-formatierten Daten anwenden können. Wir werden nun einige davon sehen: length, keys, has und map.
Die Längenfunktion
Die erste, über die wir sprechen werden, ist die Länge, mit der, wie der Name schon sagt, die Länge von Objekten, Arrays und Zeichenketten abrufen können. Die Länge von Objekten ist die Anzahl ihrer Schlüssel-Wert-Paare. Die Länge von Arrays wird durch die Anzahl der Elemente dargestellt, die sie enthalten. Die Länge einer Zeichenfolge ist die Anzahl der Zeichen, aus denen sie besteht. Mal sehen, wie man die Funktion verwendet. Angenommen, wir wollen die Länge des Arrays "characters" wissen, dann führen wir Folgendes aus:
jq '.characters | length' characters.jsonWie erwartet erhalten wir als Ergebnis 3, da es sich um die Anzahl der Elemente im Array handelt. Auf die gleiche Weise könnten wir Folgendes ausführen, um die Länge des ersten Objekts im Array zu erhalten:
jq '.characters[0] | length' characters.jsonDiesmal erhalten wir als Ergebnis 2, da es sich um die Anzahl der im Objekt enthaltenen Wertepaare handelt. Wie bereits erwähnt, gibt die gleiche Funktion, die auf eine Zeichenkette angewendet wird, die Anzahl der darin enthaltenen Zeichen zurück, sodass sie beispielsweise ausgeführt wird:
jq '.characters[0].name | length' characters.jsonAls Ergebnis erhalten wir 7, was der Länge der Zeichenkette "Aragorn" entspricht.
Die Tastenfunktion
Die Schlüsselfunktion kann auf Objekte oder Arrays angewendet werden. Im ersten Fall gibt es ein Array zurück, das
die Objektschlüssel enthält:
jq '.characters[0] | keys' characters.json
[
"name",
"race"
]Wenn es auf ein Array angewendet wird, gibt es ein anderes Array zurück, das die Indizes des ersten Arrays enthält:
jq '.characters | keys' characters.json
[
0,
1,
2
]
Die Schlüsselfunktion gibt die Elemente sortiert zurück: Wenn wir möchten, dass die Elemente in der Einfügereihenfolge zurückgegeben werden, können wir stattdessen die Funktion keys_unsorted verwenden.
Überprüfen, ob ein Objekt über einen Schlüssel verfügt
Ein sehr häufiger Vorgang, den wir für ein Objekt ausführen möchten, ist die Überprüfung, ob es einen bestimmten Schlüssel enthält. Um diese Aufgabe zu erfüllen, können wir die has Funktion verwenden. Um beispielsweise zu überprüfen, ob das Hauptobjekt unserer json-formatierten Daten den Schlüssel "weapons" enthält, könnten wir Folgendes ausführen:
jq 'has("weapons")' characters.json
falseIn diesem Fall gab die Funktion erwartungsgemäß false zurück, da das Objekt nur den Schlüssel "characters" enthält:
jq 'has("characters")' characters.json
trueWenn die Funktion auf Arrays angewendet wird, gibt sie true zurück, wenn das Array ein Element am angegebenen Index hat, andernfalls false:
jq '.characters | has(3)' characters.json
falseDas Array "characters" hat nur 3 Elemente; Arrays sind nullindiziert, so dass die Überprüfung, ob das Array als Element, das dem Index 3 zugeordnet ist, false zurückgibt.
Die Map-Funktion
Mit der map-Funktion können wir einen Filter auf jedes Element eines gegebenen Arrays anwenden. Nehmen wir zum Beispiel an, wir möchten das Vorhandensein des Schlüssels "name" in jedem der Objekte überprüfen, die im Array "characters" enthalten sind. Wir können die Karte und ihre Funktionen auf diese Weise kombinieren:
jq '.characters | map(has("name"))' characters.json
[
true,
true,
true
]
Schlüsse
In diesem Artikel kratzen wir nur an der Oberfläche der Funktionen, die das Dienstprogramm jq bietet, mit dem wir json-formatierte Daten von der Befehlszeile aus analysieren und bearbeiten können. Wir lernten die grundlegende Verwendung des Programms, wie die Operatoren "," und "|" funktionieren und wie man die Funktionen length, keys, has und map verwendet, um die Längen von Arrays, Strings und Objekten zu erhalten, Objektschlüssel oder Array-Indizes zu erhalten, zu überprüfen, ob ein Schlüssel in einem Objekt vorhanden ist oder ob ein Array ein Element am angegebenen Index hat, und wenden Sie einen Filter oder eine Funktion auf jedes Element eines Arrays an. Um zu entdecken, was jq alles kann, werfen Sie einen Blick in das Programmhandbuch!