Dans cet article, nous allons parler d’un outil que j’ai découvert récemment : jq. Cet outil permet de manipuler des données JSON facilement en ligne de commande ou dans des scripts SHELL.
L’outil est très léger, n’a pas de dépendance et permet de faire des choses assez puissante.
Avant d’avoir découvert cet outil, j’utilisais principalement les commandes sed et grep pour extraire des données formatées en JSON. Évidemment, ce n’est pas très fiable et les commandes générées peuvent vite devenir complexes. Depuis, j’utilise Jq pour extraire les données ou les reformer dans un format JSON différent que celui d’origine.
Table des matières
Présentation de Jq
Sur la page de présentation de l’outil, Jq est présenté comme l’équivalent de sed pour les données Json. Cette description est assez juste, l’outil permet de filtrer, découper, transformer et grouper des données avec la même simplicité que des outils comme sed, awk et grep.
Pour être plus précis, Jq est un programme de type filtre.
Il accepte une entrée et produit une sortie. Pour produire cette sortie, il y a de nombreux outils et filtres mis à disposition par l’outil, pour extraire/manipuler des données. Par un exemple extraire un champ particulier d’un objet Json.
Les filtres peuvent être combinés de différentes manières, il est possible de de créer des pipes (tube) : la sortie d’un filtre peut être passé à l’entrée d’un autre filtre. C’est le même fonctionnement que les pipes sur Linux qui permet de chaîner les commandes avec la barre verticale |
.
Installation de Jq
L’installation est très simple, le paquet étant dans les dépôts de la plupart des distributions Linux.
Pour une installation manuelle, avec la compilation, ce n’est pas très compliqué. Jq n’ayant pas de dépendance, c’est du code C portable. Il faut installer Make et GCC pour procéder à la compilation.
Extraire des données
Entrons dans le vif du sujet, voyons quelques exemples d’utilisation de l’outil. Je vais travailler avec l’API de cPanel qui peut renvoyer des données JSON.
Pour ce premier exemple, je vais utiliser l’API2 de cPanel et la fonction qui permet de lister les comptes de messageries présent sur un compte d’hébergement. La documentation se trouve ici.
Si je lance la commande, sans Jq, voici la sortie produite :
On constate que cela renvoi des données json mais ce n’est pas formaté, l’affichage n’est pas lisible. Cela nous donne occasion de voir le premier filtre Jq, qui ne fait rien de particulier sauf mettre en forme le Json et lui appliquer quelques couleurs.
Voici le retour qui s’affiche, pour cette fois, je mets cela en capture d’écran pour que vous puissiez voir l’indentation et les couleurs supplémentaires qui s’affichent.
L’opérateur .
est l’opérateur le plus simple qui existe, il retourne les mêmes données qu’à son entrée. Cependant par défaut, Jq indente cela correctement et ajoute des couleurs. C’est l’opérateur d’identité.
Un autre opérateur est celui qui permet de sélectionner une clé particulière dans un objet Json.
C’est l’opérateur .nomDeMaclée
, c’est possible d’en coupler plusieurs également, par exemple .nomDeClée.deuxiemeClé
.
En prenant l’exemple précédent, si je souhaite juste conserver la partie intéressante, c’est-à-dire cpanelresult->data, je peux utiliser le filtre suivant :
Avec cette commande, j’obtiens le résultat suivant :
Il est recommandé d’entourer l’expression Jq par des guillemets simple. En effet de nombreux caractères spéciaux utilisés par Jq sont également des caractères ayant une signification particulière sur les shells comme bash. Le fait de mettre des guillemets simple empêche l’interprétation de ces caractères par le shell.
Dans l’exemple précédent, nous avons donc récupéré un tableau, nous pouvons récupérer une valeur spécifique de ce tableau à l’aide des crochets [index]
. Par exemple, cela donne l’expression suivante .cpanelresult.data[0]
.
Cela nous permet de récupérer le premier élément du tableau, dans la deuxième commande, nous voyons qu’il est possible de descendre encore plus pour récupérer un élément spécifique :
Transformations
Il est également possible de transformer les données, c’est-à-dire réarranger les éléments Json de l’objet de base pour changer la manière dont il est organisé.
Si on reprend l’exemple précédent, on constate qu’on récupère pas mal d’informations pas très utiles. Par exemple, on ne voudrait que la liste des comptes emails et oublier le reste. C’est possible de faire cela avec Jq, avec trois nouvelles syntaxes :
- Le chaînage avec le pipe
|
. Permet d’itérer sur un tableau. - Le constructeur d’objet
{}
. Permet de créer un nouvel objet Json et de réarranger les données comme on le souhaite. - Le constructeur de tableau
[]
. Permet de retourner une réponse sous forme d’un tableau.
La version 1 permet de renvoyer plusieurs éléments sous la forme d’une clé : valeur. Un nouvel objet est construit avec le {}
.
Le pipe permet de faire une boucle sur tous les éléments du tableau. Par conséquent, .email
contient la valeur courante de l’itération en cours. Cela serait l’équivalent de .cpanelresult.data[i].email
, i étant le compteur de l’itération.
A noter que le caractère |
est bien entre les guillemets simple, c’est un |
interne à Jq, ce n’est pas le même |
que celui permettant de chaîner des commandes Linux (même si le fonctionnement est similaire).
La version 2 est différentes, elle est plus courte et retourne un tableau grâce à l’utilisation des crochets []
. Cette fois-ci, on récupère juste une liste d’adresses emails, sans la clé ‘email‘ contrairement à la version 1.
Boucler sur un tableau JSON en Bash avec Jq
C’était l’objet principal de cet article avant que je décide de faire une introduction plus complète à Jq. Pour finir, je vous présente une petite astuce : comment boucler sur un tableau Json, en Bash, à l’aide de Jq.
C’est pratique lorsque vous avez besoin de faire des traitements avec des commandes à chaque itération. Si vous n’avez pas besoin de faire de traitement, autant partir sur l’utilisation du pipe Jq.
Imaginons qu’on ai besoin de faire un traitement particulier sur chaque adresse email : envoyer un message. Il est possible de faire cela à l’aide d’un script bash et d’une boucle.
Décortiquons un peu ce code, il y a plusieurs choses intéressantes :
- On change l’IFS (internal field separator) par
\n
. Autrement dit, cela nous permet d’itérer sur les sauts de lignes. Par défaut, les espaces sont également pris en compte, ce qui est problématique. On stocke l’ancienne valeur dansOLD_IFS
, c’est toujours une bonne pratique de faire cela. - Ensuite, on lance la commande vu précédemment. J’ai fait exprès de prendre la version qui ne retourne pas un tableau de valeur, cela est légèrement plus complexe car le résultat est sur plusieurs lignes par défaut. L’option
-c
est ajouté à Jq, ce qui permet d’avoir une valeur du tableau sur une seule ligne. C’est là que se trouve la subtilité, on peut itérer la dessus pour parcourir toutes les valeurs du tableau ou de l’objet. - Ensuite, je récupère la valeur brute de l’email sans la clé avec le filtre
.email
. Par défaut, Jq retourne la valeur avec des guillemets autour. Pour éviter de faire uncut
on peut ajouter l’option-r
. (qui signifie raw et enlève les guillemets) - Enfin, je fais mon traitement sur la valeur concernée, ici l’envoi d’un message.
Conditions
Avec Jq, il est également possible de créer des conditions, par exemple, afficher un objet que si une certaine condition est respectée.
Pour illustrer cela, je viens de suspendre un compte email. Par conséquent suspended_login et suspended_incoming ont une valeur à 1 pour une adresse email. Les autres adresses ont toujours des valeurs à 0. Si je veux récupérer cette adresse en particulier, je peux utiliser deux options de Jq :
- select : qui permet de récupérer un élément si la condition qui lui est passé est validé
- une condition : qui permet de tester une valeur de l’objet json par exemple
Dans cette commande, j’itère sur la liste des comptes emails retournés par l’api cPanel grâce à l’utilisation du pipe à l’intérieur de Jq (celui entre guillemet donc). Ensuite, j’utilise select qui permet de retourner l’objet si la condition qui lui est passé est vraie. J’obtiens le résultat suivant :
Conclusion
Ce n’est qu’une présentation rapide de l’outil. Jq est bien plus puissant que les exemples présentés dans cet article. C’est un outil à garder sous le coude pour éviter de faire des commandes monstrueuse avec sed, grep et awk.
Pour approfondir le sujet, je vous recommande fortement de lire l’article de Stéphane Bortzmeyer : il évoque tout un aspect statistique que je n’ai pas utilisé.
La page de manuel de l’outil qui est très complète et bien faites également.
Une réponse sur « Manipuler facilement du JSON en ligne de commande avec Jq »
Bonjour,
Je pense que a la fin de la partie extraire les données ils y a une erreur.
Le résultat :
// Sortie de la deuxième commande
« [email protected] »
Devrait être pour la requête :
cpapi2 –output=json –user=mondev Email listpops | jq ‘.cpanelresult.data[2].email’ et non la requête cpapi2 –output=json –user=mondev Email listpops | jq ‘.cpanelresult.data[0].email’