🐤 jq
Manipuler du JSON depuis un terminal : filtrer des réponses API, inspecter des logs, transformer des payloads.
jq (JSON processor) est l’outil de référence pour le traitement JSON en ligne de commande. Syntaxe déclarative, composition de filtres, zéro dépendance.
Installation
# Debian / Ubuntu
sudo apt install jq
# macOS (Homebrew)
brew install jq
# Alpine
apk add jqFlags essentiels
| Flag | Rôle |
|---|---|
-r | Sortie brute (sans guillemets autour des chaînes) |
-c | Sortie compacte (une ligne) |
-s (slurp) | Rassembler toute l’entrée en un tableau |
-S | Trier les clés des objets |
-M | Sortie en mode couleurs (pour less -R) |
# Extraction simple — les chaînes sont entre guillemets
echo '{"nom": "Arthur"}' | jq '.nom'
# → "Arthur"
# Sortie brute
echo '{"nom": "Arthur"}' | jq -r '.nom'
# → ArthurPiège : sans
-r, jq retourne les chaînes entre guillemets doubles. Pour passer le résultat à une autre commande (echo,ssh,curl), ajouter-r.
Accès aux champs
| Syntaxe | Résultat |
|---|---|
.champ | Valeur du champ champ |
.champ | keys | Liste des clés de l’objet |
.[0] | Premier élément du tableau |
.[] | Dépiler chaque élément |
.champ1.champ2 | Champ imbriqué |
.champ["clé"] | Clé avec caractères spéciaux |
.[]?.champ | Accès sûr (saute null) |
# Objet imbriqué
echo '{"user": {"nom": "Alice", "âge": 30}}' | jq '.user.nom'
# → "Alice"
# Tableau d'objets — dépiler tous les noms
echo '[{"nom": "A"}, {"nom": "B"}]' | jq '.[].nom'
# → "A"
# → "B"
# Accès sûr : ne pas planter si le champ est manquant
echo '{"nom": "A"}' | jq '.prénom // "inconnu"'
# → "inconnu"Filtres fondamentaux
select — filtrer les éléments
# Ne garder que les éléments où le champ satisfait la condition
echo '[1, 5, 10, 15]' | jq '[.[] | select(. > 8)]'
# → [10, 15]
# Dans un flux d'objets
echo '[{"role": "admin"}, {"role": "user"}]' | jq '.[] | select(.role == "admin")'
# → {"role": "admin"}map — transformer chaque élément
# Doubler chaque nombre
echo '[1, 2, 3]' | jq 'map(. * 2)'
# → [2, 4, 6]
# Extraire un champ de chaque objet
echo '[{"nom": "A", "id": 1}, {"nom": "B", "id": 2}]' | jq 'map(.nom)'
# → ["A", "B"]group_by — regrouper
echo '[
{"dept": "vendeur", "rev": 100},
{"dept": "dev", "rev": 200},
{"dept": "vendeur", "rev": 150}
]' | jq 'group_by(.dept)'
# → [[{...vendeur, 100}, {...vendeur, 150}], [{...dev, 200}]]
group_bytrie d’abord le tableau par la clé, puis groupe les éléments adjacents.
flatten — aplatir les tableaux imbriqués
echo '[[1, 2], [3, 4]]' | jq 'flatten'
# → [1, 2, 3, 4]
echo '[[1, [2, 3]], [4]]' | jq 'flatten(2)'
# → [1, 2, 3, 4] (profondeur 2)reduce — accumuler
# Somme des éléments
echo '[1, 2, 3, 4]' | jq 'reduce .[] as $x (0; . + $x)'
# → 10
# Construire un objet depuis un tableau
echo '[{"k": "a", "v": 1}, {"k": "b", "v": 2}]' | \
jq 'reduce .[] as $item ({}; .[$item.k] = $item.v)'
# → {"a": 1, "b": 2}Fonctions pratiques
| Fonction | Rôle |
|---|---|
length | Longueur d’une chaîne, taille d’un tableau, nombre de clés |
any(cond) | Vrai si au moins un élément satisfait la condition |
all(cond) | Vrai si tous les éléments satisfont la condition |
index(val) | Index de val dans le tableau, ou null |
IN(val; arr...) | Vrai si val est dans l’un des arguments |
test(regex) | Vrai si la chaîne correspond à l’expression régulière |
contains(str) | Vrai si la chaîne contient str |
ascii_downcase | Minuscules (ASCII uniquement) |
ascii_upcase | Majuscules (ASCII uniquement) |
empty | Ne produit aucune valeur (filtre tout) |
# Longueur
echo '[1, 2, 3]' | jq 'length'
# → 3
# any / all
echo '[true, false, true]' | jq 'any'
# → true
echo '[true, true]' | jq 'all'
# → true
# index
echo '[10, 20, 30]' | jq 'index(20)'
# → 1
# test — vérifier un motif
echo '"bonjour@exemple.fr"' | jq 'test("@.*\\.fr$")'
# → true
# IN — appartenance
echo '"b"' | jq 'IN("a"; "b"; "c")'
# → trueUsages courants
Extraire un champ d’un fichier JSON
# Un seul objet
jq '.data.id' fichier.json
# Chaque élément d'un tableau
jq '.items[].name' fichier.jsonManipuler un payload pour curl
# Construire un JSON pour POST
jq -n --arg nom "Alice" --argjson âge 30 \
'{nom: $nom, âge: $âge}'
# → {"nom":"Alice","âge":30}Filtrer les logs JSON (Docker, Kubernetes)
# Entrées en erreur
docker logs --since 1h container | jq -r 'select(.level == "error") | .message'
# Compter les niveaux de log
docker logs container | jq -s 'group_by(.level) | map({niveau: .[0].level, count: length})'Modifier un champ dans un fichier (édition in-place)
# Attention : remplace le fichier original
jq '.config.timeout = 30' config.json > tmp.json && mv tmp.json config.jsonPipelines Kubernetes et Docker
| Besoin | Commande |
|---|---|
| Noms des pods | kubectl get pods -o json | jq -r '.items[].metadata.name' |
| IPs des pods | kubectl get pods -o json | jq -r '.items[].status.podIP' |
| Containers en erreur | kubectl get pods -o json | jq -r '.items[] | select(.status.containerStatuses[]?.state.waiting != null) | .metadata.name' |
| Logs formatés JSON | docker logs --format json container | jq -r '"\(.time) [\(.level)] \(.message)"' |
Pitfalls
- Clés avec espaces ou tirets : utiliser la notation
.uniquement pour les identifiants valides. Pour.my-keyou.my key, écrire."my-key"ou["my key"]. - Sortie blanche dans un pipeline : oublier
-rquand on passe jq àecho,ssh, ouxargs. - Slurp (
-s) sur un gros fichier : tout charge en mémoire. Préférer le pipe pour les fichiers volumineux. - Indentation dans
jq -n: utiliser--indent 0pour éviter le reformatage du JSON produit. - Comparaison de chaînes :
==fonctionne sur les strings, maistestest nécessaire pour les regex. - Fusion d’objets :
{a, b} + {c}fusionne, mais+sur les tableaux concatène.*pour fusionner récursivement.