J'ai bin chercher une solution à ma question, mais je n'ai pas trouvé de réponse ou mieux dit que je ne l'ai pas trouvée avec ce que j'ai trouvé. Permet donc de parler de mon problème. J'utilise un logiciel de contrôle de maison intelligente sur un Raspberry Pi et, comme je l'ai découvert ce week-end en utilisant la réception pilight, je peux capturer les données de mon capteur de température extérieure. La sortie de pilight-receive ressemble à ceci:
{
"message": {
"id": 4095,
"temperature": 409.5
},
"Origin": "receiver",
"protocol": "alecto_wsd17",
"uuid": "0000-b8-27-eb-0f3db7",
"repeats": 3
}
{
"message": {
"id": 1490,
"temperature": 25.1,
"humidity": 40.0,
"battery": 1
},
"Origin": "receiver",
"protocol": "alecto_ws1700",
"uuid": "0000-b8-27-eb-0f3db7",
"repeats": 3
}
{
"message": {
"id": 2039,
"temperature": 409.5
},
"Origin": "receiver",
"protocol": "alecto_wsd17",
"uuid": "0000-b8-27-eb-0f3db7",
"repeats": 4
}
Maintenant, ma question est la suivante: comment diable puis-je extraire la température et l'humidité de l'endroit où se trouve l'ID 1490. Et comment me recommanderiez-vous de vérifier cela fréquemment? Par un travail cron qui s'exécute toutes les 10 minutes, crée une sortie de la réception pilight, extrait les données de la sortie et les pousse vers l'API Smart Home Control.
Quelqu'un a une idée - merci beaucoup
Vous pouvez utiliser jq
pour traiter les fichiers json dans Shell.
Par exemple, j'ai enregistré votre exemple de fichier json en tant que raul.json
puis a couru:
$ jq .message.temperature raul.json
409.5
25.1
409.5
$ jq .message.humidity raul.json
null
40
null
jq est disponible pré-emballé pour la plupart des distributions Linux.
Il y a probablement un moyen de le faire dans jq
lui-même, mais le moyen le plus simple que j'ai trouvé pour obtenir les deux valeurs souhaitées sur une seule ligne est d'utiliser xargs
. Par exemple:
$ jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json | xargs
25.1 40
ou, si vous souhaitez parcourir chaque .message.id
instance, nous pouvons ajouter .message.id
à la sortie et utilisez xargs -n 3
car nous savons qu'il y aura trois champs (id, température, humidité):
jq '.message.id, .message.temperature, .message.humidity' raul.json | xargs -n 3
4095 409.5 null
1490 25.1 40
2039 409.5 null
Vous pouvez ensuite post-traiter cette sortie avec awk ou autre chose.
Enfin, python et Perl ont d'excellentes bibliothèques pour analyser et manipuler les données json. Comme le font plusieurs autres langages, y compris php et Java.
Mon outil de choix pour le traitement de JSON sur la ligne de commande est jq. Cependant, si jq n'est pas installé, vous pouvez très bien faire avec Perl:
# Perl -MJSON -e '$/ = undef; my $data = <>; for my $hash (new JSON->incr_parse($data)) { my $msg = $hash->{message}; print "$msg->{temperature} $msg->{humidity}\n" if $msg->{id} == 1490 }' < data.json
25.1 40
Pour ceux qui ne comprennent pas le awk
avancé comme ils le voudraient (comme les gens comme moi) et qui n'ont pas jq
pré-installé, une solution simple serait la tuyauterie quelques commandes natives ensemble comme ceci:
grep -A2 '"id": 1490,' stats.json | sed '/1490/d;s/"//g;s/,//;s/\s*//'
Si vous essayez seulement d'obtenir les valeurs, il est plus facile d'utiliser simplement grep
plutôt que awk
ou sed
:
grep -A2 '"id": 1490,' stats.json | grep -o "[0-9]*\.[0-9]*"
Pour fournir une explication, cela me semble être le moyen le plus simple.
grep -A2
saisit la ligne que vous recherchez dans le JSON avec les 2 lignes suivantes, qui contiennent la température et l'humidité.grep -o
imprime simplement uniquement les chiffres numériques séparés par un .
(qui ne se produira jamais le premier 1490
ligne, vous êtes donc laissé avec vos 2 valeurs - température et humidité. Très simple. Encore plus simple que d'utiliser jq
, à mon avis.votre sortie est un ensemble d'extraits JSON plutôt qu'un JSON complet. Si/une fois que vous réorganisez votre sortie pour qu'elle soit un JSON intégral, par ex. comme ceci (en supposant que votre sortie est dans file.json
):
echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]"
alors il est facile de réaliser ce que vous voulez avec l'outil jtc
(disponible sur: https://github.com/ldn-softdev/jtc ):
bash $ echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]" | jtc -x "[id]:<1490>d [-1]" -y[temperature] -y[humidity] -l
"temperature": 25.1
"humidity": 40.0
bash $
dans l'exemple ci-dessus drop -l
si vous ne voulez pas d'étiquettes imprimées
jq
est de loin la solution la plus élégante. Avec awk
vous pourriez écrire
awk -v id=1490 '
$1 == "\"id\":" && $2 == id"," {matched = 1}
$1 == "}," {matched = 0}
matched && $1 ~ /temperature|humidity/ {sub(/,/,"", $2); print $2}
' file