Comment extraire des données d'un fichier JSON

13

J'ai bin à la recherche d'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 pour vous: comment diable puis-je extraire la température et l'humidité de l'endroit où l'id est 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

Raul Garcia Sanchez
la source
3
Le format semble être JSON . Il existe de nombreuses façons d'analyser JSON. Cela dépend de ce que vous êtes à l'aise. Python? JavaScript? Autre chose?
muru
Je connais un peu de Python et un peu de JavaScript surtout je connais C ++ et C #. Mais après avoir vu toutes les commandes awk et sed, je pensais que la commande devait être facile xD
Raul Garcia Sanchez
1
Ce n'est pas difficile avec awket à sedcondition que la sortie JSON conserve le formatage montré ici, ce qui n'est pas nécessaire - les espaces blancs n'ont pas d'importance pour JSON. Par exemple, cette awkcommande: awk '/temperature|humidity/ {print $2}'est proche.
muru
4
avec l' ksh93analyse json est intégré à read.
mikeserv
1
vérifiez les rétroportations sifflantes. il pourrait être là, vous épargnant une mise à niveau vers jessie (sauf si vous envisagez de mettre à niveau de toute façon). aha! il est rétroporté vers Wheezy. packages.debian.org/wheezy-backports/jq
cas

Réponses:

22

Vous pouvez utiliser jqpour traiter les fichiers json en shell.

Par exemple, j'ai enregistré votre exemple de fichier json sous raul.json, puis j'ai exécuté:

$ 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 en jqsoi, 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 voulez parcourir chaque .message.idinstance, nous pouvons ajouter .message.idà la sortie et utiliser xargs -n 3car 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 langues, dont php et java.

cas
la source
2
spécifiquement,jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
glenn jackman
1
ou, en bash,{ read temp; read hum; } < <(jq ...)
glenn jackman
1
Voir ma réponse qui utilise simplement grep. Il peut ne pas fonctionner pour certaines versions spécifiques de grep, mais il est plus simple que jqdans ce scénario, même s'il jqest conçu spécifiquement pour l'analyse JSON. J'ai quand même donné jqune réponse positive à la réponse. C'est en effet un outil pour le travail, mais parfois vous pouvez simplement retirer les agrafes avec vos doigts plutôt que de chercher un décapant d'agrafes.
rubynorails
2
json ne peut pas être analysé de manière fiable avec des expressions régulières, pas plus que xml ou html. et la plupart des données json (par exemple récupérées via une API Web) ne sont pas bien formatées avec des sauts de ligne et une indentation supplémentaires. pour analyser json de manière fiable, vous avez besoin d'un analyseur json. jqen fait partie pour les scripts shell. d'autres langues ont des bibliothèques d'analyse json.
cas
1
tout peut être analysé de manière fiable avec des expressions régulières. cela dépend simplement du nombre que vous utilisez. comment pensez-vous que jqcela fonctionne?
mikeserv
0

jqest de loin la solution la plus élégante. Avec awkvous pourriez écrire

awk -v id=1490 '
    $1 == "\"id\":" && $2 == id"," {matched = 1}
    $1 == "}," {matched = 0}
    matched && $1 ~ /temperature|humidity/ {sub(/,/,"", $2); print $2}
' file
glenn jackman
la source
0

Pour ceux qui ne comprennent pas le niveau avancé awkaussi bien qu'ils le souhaiteraient (comme des gens comme moi) et qui n'ont pas de jqpré-installation, une solution simple serait de canaliser 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 grepplutôt que awkou 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.

  • Le grep -A2saisit la ligne que vous recherchez dans le JSON avec les 2 lignes suivantes, qui contiennent la température et l'humidité.
  • Le tuyau grep -oimprime simplement uniquement des chiffres numériques séparés par un .(qui ne se produira jamais sur la première 1490ligne, donc vous vous retrouvez avec vos 2 valeurs - température et humidité. Très simple. Encore plus simple que d'utiliser jq, à mon avis.
rubynorails
la source
0

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
nwk
la source
0

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 exemple comme ceci (en supposant que votre sortie est dedans file.json):

echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]"

alors il est facile de réaliser ce que vous voulez avec l' jtcoutil (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, supprimez -lsi vous ne voulez pas d'étiquettes imprimées

Dmitry L.
la source