jq est un processeur JSON en ligne de commande, elle permet de transformer un document JSON en un autre. Certaines options permettent de convertir depuis et vers d’autres formats.
Les bases de la commande jq
La syntaxe est la suivante :
jq [options…] filtre [fichier…]
mais vous l’utiliserez sans doute plus souvent sous la forme :
UN_FLUX | jq [options…] filtre
ou encore :
jq [options…] filtre <<<"${UNE_VARIABLE}"
Le filtre le plus simple est .
, il indique que l’on prend tout le flux d’entrée permettant de mettre en forme le document ou de le valider, par exemple :
echo '{"A":"a","B":true,"C":42}' | jq .
Documentation jq
Notez que le résumé produit en invoquant jq avec l’option -h
ne fournit pas une liste complète des options.
Pour connaître toutes les options supportées il faut se référer au manuel : jq Manual en anglais qui est disponible pour chacune des versions de jq (actuellement la dernière version est : 1.6).
Deux options non documentées sont à noter :
- --debug-dump-disasm
- --debug-trace
Il est également possible d’avoir la documentation depuis le terminal à l’aide de :
man jq
Aide-mémoire
Prenons l’exemple suivant :
{
"id": 42,
"group-id": 42,
"name": "nom du group",
"group": [
{ "name":"nom un", "admin": false, "total": 100},
{ "name":"nom deux", "admin": true, "total": 200},
{ "name":"nom trois", "admin": false, "total": 300}
]
}
que vous mettrez dans la variable TEST
à l’aide de :
TEST=$( cat <<EOF | jq -c .
{
"id": 42,
"group-id": 42,
"name": "nom du group",
"group": [
{ "name":"nom un", "admin": false, "total": 100},
{ "name":"nom deux", "admin": true, "total": 200},
{ "name":"nom trois", "admin": false, "total": 300}
]
}
EOF
)
Pour voir le contenu du flux JSON :
jq . <<<"${TEST}"
Pour extraire les attributs de premier niveau id
et name
:
jq '{"id", "name"}' <<<"${TEST}"
Notez que si le nom de l’attribut contient un caractère spécial comme le signe moins, vous devez utiliser la notation complète :
jq '{"group-id": .["group-id"], "name": .name}' <<<"${TEST}"
Pour extraire les attributs name
et total
de chacune des entrées du tableau :
jq '.group[] | {name, total}' <<<"${TEST}"
Pour extraire les attributs name
et total
au format texte au lieu du format JSON :
jq -r '.group[] | {name, total} | join(" ")' <<<"${TEST}"
Filtrer les résultats en fonction de la valeur d’un attribut :
jq '.group[] | select(.name == "nom deux") | {total}' <<<"${TEST}" # Au format JSON
jq '.group[] | select(.name == "nom deux") | .total' <<<"${TEST}" # Au format texte (c’est un nombre, le paramètre -r n’est pas nécessaire)
Filtrer avec plusieurs conditions :
jq '.group[] | select( (.name | contains("nom")) and (.total > 150))' <<<"${TEST}"
Filtrer avec l’opérateur not
(inversion de la condition) :
jq -c '.group[] | select(.name | test("x") | not)' <<<"${TEST}" # récupère toutes les entrées pour les noms ne contenant pas de x
Pour obtenir un tableau JSON :
jq '[ .group[] | select(.name | test("x") | not) ]' <<<"${TEST}"
Construire un document JSON à partir d’un fichier texte simple
Le fichier /proc/meminfo
contiens les informations liées à la mémoire de votre machine.
cat /proc/meminfo
donne quelque chose comme :
MemTotal: 8000520 kB
MemFree: 7354312 kB
MemAvailable: 7795420 kB
Buffers: 52624 kB
Cached: 467256 kB
SwapCached: 0 kB
…
Si on souhaite transformer cet extrait en JSON, on va chercher à réécrire le texte ci-dessus comme suit :
{
"MemTotal": "8000520 kB",
"MemFree": "7353556 kB",
"MemAvailable": "7794852 kB",
"Buffers": "52632 kB",
"Cached": "467436 kB",
"SwapCached": "0 kB"
}
Si on cherche à produire naïvement ce fichier, il va falloir en particulier gérer le caractère ,
de manière particulière car, il est présent à la fin de toutes les lignes sauf la dernière.
On va plutôt chercher à créer le plus rapidement des objets JSON et les manipuler pour obtenir le résultat attendu.
La première étape sera donc de transformer :
MemTotal: 8000520 kB
en :
{"MemTotal":"8000520 kB"}
Pour ce genre de situation, la commande sed
est parfaite.
-
Explications de la syntaxe utilisée pour sed
sed
sed
est un éditeur de flux pour le filtrage et la transformation de texte. On utilise un éditeur de flux pour effectuer des transformations de texte basiques sur un flux d’entrée (un fichier ou l’entrée d’un tube) et essentiellement dans le cadre d’une édition scriptée.echo 'ABC:DEF' | sed -E 's/^(.*):(.*)$/{"\1":"\2"}/'
On utilise l’option
-E
(qui peut s’écrire également-r
ou--regexp-extended
) permettant d’utiliser les expressions régulières étendues.Explication de la transformation
s/^(.*):(.*)$/{"\1":"\2"}/
:s
: Indique que l’on souhaite faire une substitution./
: Le séparateur du motif et de la chaîne de remplacement qui sera utilisé.^(.*):(.*)$
: Le motif à rechercher/
: Le séparateur{"\1":"\2"}
: la chaîne de substitution./
: Le dernier séparateur qui sans option indique que la substitution ne doit se faire qu’une seule fois par ligne.
Explication du motif
^(.*):(.*)$
:^
: indique que la suite du motif doit se trouver immédiatement en début de ligne.(.*)
: défini un groupe avec les parenthèses et ce groupe est composé de n’importe quel caractère (.
) un nombre quelconque de fois (*
).- ‘:’ : recherche le caractère
:
(.*)
: défini un groupe second groupe (avec les mêmes règles)$
: indique que la fin de la ligne.
Pour résumé indique à
sed
de considérer ce qui est au début de la ligne et ce jusqu’au caractère:
comme un groupe et ce qu’il y a après ce caractère jusqu’à la fin comme un autre.Explication de la substitution
{"\1":"\2"}
:{
: écrit le caractère{
"
: écrit le caractère"
\1
: écrit le contenu du 1er groupe"
: écrit le caractère"
:
: écrit le caractère:
"
: écrit le caractère"
\2
: écrit le contenu du 2nd groupe"
: écrit le caractère"
}
: écrit le caractère}
Mais vous aurez noté qu’en réalité, on voudrait traiter la chaîne
ABC: DEF
et non pas uniquementABC:DEF
. Pour cela il faut utiliser le commutateur[[:blank:]]
(qui correspond à l’espace, mais aussi au caractère de tabulation) suivit du caractère*
pour indiquer un nombre quelconque de fois.Et cela donne:
echo 'ABC: DEF' | sed -E 's,^(.*):[[:blank:]]*(.*)$,{"\1":"\2"},'
Notez que j’ai changé le séparateur
,
au lieu de/
car je trouve cela plus lisible et qu’en faitsed
se fiche du caractère, du moment que c’est le même que vous utiliser pour toute l’expression.
Du coup, nous avons :
cat /proc/meminfo | sed -E 's,^(.*):[[:blank:]]*(.*)$,{"\1":"\2"},'
qui produit le résultat :
{"MemTotal":"16233556 kB"}
{"MemFree":"637928 kB"}
{"MemAvailable":"2792636 kB"}
{"Buffers":"211828 kB"}
{"Cached":"5106280 kB"}
{"SwapCached":"5608 kB"}
Cela correspond à une succession d’objets JSON qu’il faudra mettre dans un seul objet JSON.
Pour commencer utilisons l’option --slurp
de jq
:
cat /proc/meminfo | sed -E 's,^(.*):[[:blank:]]*(.*)$,{"\1":"\2"},' | jq --slurp -c '.'
Cela donnera un tableau avec l’ensemble des lignes construites.
[{"MemTotal":"16233556 kB"},{"MemFree":"1630228 kB"},{"MemAvailable":"3154656 kB"},{"Buffers":"190940 kB"},{"Cached":"4336756 kB"},{"SwapCached":"6320 kB"}]
Il ne reste plus qu’à combiner tous ces objets dans un seul et comme on a un tableau la commande add
de jq est parfaitement adaptée :
cat /proc/meminfo | sed -E 's,^(.*):[[:blank:]]*(.*)$,{"\1":"\2"},' | jq --slurp 'add'
Et voilà :
{
"MemTotal": "8000520 kB",
"MemFree": "7353556 kB",
"MemAvailable": "7794852 kB",
"Buffers": "52632 kB",
"Cached": "467436 kB",
"SwapCached": "0 kB"
}
Si vous souhaitez composer à votre
cat /proc/meminfo | head -n 3 | sed -E 's,^(.*):[[:blank:]]*(.*)$,{"\1":"\2"},' | jq --slurp -S '{ "memory": add }'
{
"memory": {
"MemAvailable": "3230012 kB",
"MemFree": "1216544 kB",
"MemTotal": "16233556 kB"
}
}
Autre billets et documentations :
- 👍 Installation de jq
- 🎓 Aide-mémoire jq : map(x)
- jq Manual
- Aide mémoire pour:
- Understanding Sed command with extended RegExp
- how to pass json file name to jq?
ᦿ