Cómo extraer datos de un archivo JSON

13

Tengo bin buscando una solución para mi pregunta, pero no encontré una o mejor, dije que no la obtuve con lo que encontré. Así que hablemos de cuál es mi problema. Estoy usando un software de control inteligente para el hogar en una Raspberry Pi y, como descubrí este fin de semana usando la recepción de pilight, puedo capturar los datos de mi sensor de temperatura exterior. La salida de Pilight-Recibir se ve así:

{
        "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
}

Ahora mi pregunta para usted: ¿Cómo diablos puedo extraer la temperatura y la humedad de donde está la identificación 1490. ¿Y cómo me recomendaría que verifique esto con frecuencia? Mediante un trabajo cron que se ejecuta cada 10 minutos, crea una salida de la recepción de pilight, extrae los datos de la salida y los envía a la API de Smart Home Control.

Alguien que tiene una idea, muchas gracias

Raul Garcia Sanchez
fuente
3
El formato parece ser JSON . Hay muchas formas de analizar JSON. Depende de con qué se sienta cómodo. ¿Pitón? JavaScript? ¿Algo más?
muru
Sé un poco de Python y un poco de JavaScript, principalmente sé C ++ y C #. Pero después de ver todos los comandos awk y sed, pensé que debía ser un comando fácil xD
Raul Garcia Sanchez
1
No es difícil awky sedsiempre que la salida JSON conserve el formato que se muestra aquí, lo cual no es necesario; el espacio en blanco no importa para JSON. Por ejemplo, este awkcomando: awk '/temperature|humidity/ {print $2}'está cerca.
muru
44
con ksh93análisis json está incorporado a read.
mikeserv
1
compruebe los puertos de salida de wheezy. podría estar allí, ahorrándote una actualización a jessie (a menos que planearas actualizar de todos modos). aha! se retrocede a wheezy. packages.debian.org/wheezy-backports/jq
cas

Respuestas:

22

Puede usar jqpara procesar archivos json en shell.

Por ejemplo, guardé su archivo json de muestra como raul.jsony luego ejecuté:

$ jq .message.temperature raul.json 
409.5
25.1
409.5
$ jq .message.humidity raul.json 
null
40
null

jq está disponible preempaquetado para la mayoría de las distribuciones de Linux.

Probablemente hay una forma de hacerlo en jqsí mismo, pero la forma más simple que encontré para obtener los dos valores deseados en una línea es usar xargs. Por ejemplo:

$ jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json | xargs
25.1 40

o, si desea recorrer cada .message.idinstancia, podemos agregar .message.ida la salida y usar, xargs -n 3ya que sabemos que habrá tres campos (id, temperatura, humedad):

jq '.message.id, .message.temperature, .message.humidity' raul.json | xargs -n 3
4095 409.5 null
1490 25.1 40
2039 409.5 null

Luego, puede procesar posteriormente esa salida con awk o lo que sea.


Finalmente, tanto python como perl tienen excelentes bibliotecas para analizar y manipular datos json. Al igual que varios otros idiomas, incluidos php y java.

cas
fuente
2
específicamente,jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
Glenn Jackman
1
o, en bash,{ read temp; read hum; } < <(jq ...)
Glenn Jackman
1
Vea mi respuesta que simplemente usa grep. Puede que no funcione para algunas versiones específicas de grep, pero es más sencillo que jqen este escenario, aunque jqestá diseñado específicamente para analizar JSON. Sin embargo, le di jqun voto positivo a la respuesta. De hecho, es una herramienta para el trabajo, pero a veces simplemente puede quitar las grapas con los dedos en lugar de buscar un quitagrapas.
rubynorails
2
json no se puede analizar de manera confiable con expresiones regulares más que xml o html. y la mayoría de los datos json (por ejemplo, obtenidos a través de una API web) no están bien formateados con saltos de línea adicionales y sangría. para analizar json de manera confiable, necesita un analizador json. jqes uno de los scripts de shell. otros idiomas tienen bibliotecas de análisis json.
cas
1
cualquier cosa se puede analizar de manera confiable con expresiones regulares. sólo depende de la forma en que muchos se utiliza. como crees jqque lo hace
mikeserv
0

jqEs, con mucho, la solución más elegante. Contigo awkpodrías escribir

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

Para aquellos que no entienden el avanzado awktan bien como les gustaría (como personas como yo) y no tienen jqpreinstalado, una solución fácil sería unir un par de comandos nativos de la siguiente manera:

grep -A2 '"id": 1490,' stats.json | sed '/1490/d;s/"//g;s/,//;s/\s*//'

Si solo está tratando de obtener los valores, es más fácil usar en greplugar de awko sed:

grep -A2 '"id": 1490,' stats.json | grep -o "[0-9]*\.[0-9]*"

Para dar una explicación, esta me parece la forma más simple.

  • La grep -A2agarra la línea que busca en el JSON, junto con las 2 líneas siguientes, que contienen la temperatura y la humedad.
  • La tubería grep -osimplemente imprime solo dígitos numéricos separados por un .(lo que nunca ocurrirá en la primera 1490línea, por lo que le quedan sus 2 valores: temperatura y humedad. Muy simple. Incluso más simple que usar jq, en mi opinión.
rubynorails
fuente
0

Mi herramienta de elección para procesar JSON en la línea de comando es jq. Sin embargo, si no tiene instalado jq, puede hacerlo bastante bien con 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
fuente
0

su salida es un conjunto de fragmentos JSON en lugar de un JSON completo. Si / una vez que reorganiza su salida para que sea un JSON integral, por ejemplo, así (suponiendo que su salida esté en file.json):

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

entonces es fácil lograr lo que desea con la jtcherramienta (disponible en: 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 $ 

en el ejemplo anterior, suelte -lsi no desea etiquetas impresas

Dmitry L.
fuente