Cómo analizar archivos JSON en la línea de comandos de Linux con jq

Un indicador de terminal en una PC con Linux.
Fatmawati Achmad Zaenuri / Shutterstock

JSON es uno de los formatos más populares para transferir datos basados ​​en texto en la web. Está en todas partes y seguramente te lo encontrarás. Le mostraremos cómo manejarlo desde la línea de comandos de Linux usando el jqcomando.

JSON y jq

JSON son las siglas de JavaScript Object Notation . Es un esquema que permite codificar datos en archivos de texto sin formato, de una manera autodescriptiva. No hay comentarios en un archivo JSON; el contenido debe ser autoexplicativo. Cada valor de datos tiene una cadena de texto llamada «nombre» o «clave». Esto le dice cuál es el valor de los datos. Juntos, se conocen como pares nombre: valor o pares clave: valor. Dos puntos ( :) separan una clave de su valor.

Un «objeto» es una colección de pares clave: valor. En un archivo JSON, un objeto comienza con una llave abierta ( {) y termina con una llave de cierre ( }). JSON también admite «matrices», que son listas ordenadas de valores. Una matriz comienza con un corchete de apertura ( [) y termina con un corchete de cierre ( ]).

De estas definiciones simples, por supuesto, puede surgir una complejidad arbitraria. Por ejemplo, los objetos se pueden anidar dentro de los objetos. Los objetos pueden contener matrices y las matrices también pueden contener objetos. Todos los cuales pueden tener niveles abiertos de anidación.

En la práctica, sin embargo, si el diseño de los datos JSON es complicado, el diseño del diseño de los datos probablemente debería ser reconsiderado. Por supuesto, si no está generando los datos JSON, simplemente tratando de usarlos, no tiene voz en su diseño. En esos casos, desafortunadamente, solo tienes que lidiar con eso.

La mayoría de los lenguajes de programación tienen bibliotecas o módulos que les permiten analizar datos JSON. Lamentablemente, el shell Bash no tiene esa funcionalidad .

Sin embargo, siendo la necesidad la madre de la invención, jqnació la utilidad. Con jq, podemos  analizar fácilmente JSON en el shell Bash. Y no importa si tiene que trabajar con JSON elegante y bien diseñado, o con el material de que están hechas las pesadillas.

Cómo instalar jq

Tuvimos que instalarlo jq en todas las distribuciones de Linux que usamos para investigar este artículo.

Para instalar jqen Ubuntu, escriba este comando:

sudo apt-get install jq

El comando "sudo apt-get install jq" en una ventana de terminal.

Para instalar jqen Fedora, escriba este comando:

sudo dnf instalar jq

El comando "sudo dnf install jq" en una ventana de terminal.

Para instalar jqen Manjaro, escriba este comando:

sudo pacman -Sy jq

El comando "sudo pacman -Sy jq" en una ventana de terminal.

Cómo hacer que JSON sea legible

JSON no se preocupa por los espacios en blanco y el diseño no lo afecta. Siempre que siga las reglas de la gramática JSON , los sistemas que procesan JSON pueden leerlo y comprenderlo. Debido a esto, JSON a menudo se transmite como una cadena larga y simple, sin tener en cuenta el diseño. Esto ahorra un poco de espacio porque las pestañas, los espacios y los caracteres de nueva línea no tienen que incluirse en el JSON. Por supuesto, la desventaja de todo esto es cuando un humano intenta leerlo.

Saquemos un objeto JSON corto del  sitio de la  NASA que nos dice la posición de la Estación Espacial Internacional . Usaremos curl, que puede descargar archivos  para recuperar el objeto JSON por nosotros.

No nos importa ninguno de los mensajes de estado que  curl generalmente se generan, por lo que escribiremos lo siguiente, usando la -sopción (silenciosa):

curl -s http://api.open-notify.org/iss-now.json

El comando "curl -s http://api.open-notify.org/iss-now.json" en una ventana de terminal.

Ahora, con un poco de esfuerzo, puedes leer esto. Tienes que elegir los valores de los datos, pero no es fácil ni conveniente. Repitamos esto, pero esta vez lo pasaremos jq.

jqusa filtros para analizar JSON, y el más simple de estos filtros es un punto ( .), que significa «imprimir el objeto completo». De forma predeterminada, jq imprime bastante la salida.

Lo juntamos todo y escribimos lo siguiente:

curl -s http://api.open-notify.org/iss-now.json | jq.

El "curl -s http://api.open-notify.org/iss-now.json | jq".  comando en una ventana de terminal.

¡Eso está mucho mejor! Ahora, podemos ver exactamente lo que está pasando.

Relacionado:  Cómo eliminar un usuario en Linux (y eliminar todos los rastros)

Todo el objeto está envuelto en llaves. Contiene dos claves: pares de nombres: messagey timestamp. También contiene un objeto llamado iss_position, que contiene dos pares clave: valor:  longitudey latitude.

Intentaremos esto una vez más. Esta vez escribiremos lo siguiente y redirigiremos la salida a un archivo llamado «iss.json»:

curl -s http://api.open-notify.org/iss-now.json | jq. > iss.json
gato iss.json

Los comandos "curl -s http://api.open-notify.org/iss-now.json | jq.> Iss.json" y "cat iss.json" en una ventana de terminal.

Esto nos da una copia bien distribuida del objeto JSON en nuestro disco duro.

Acceder a valores de datos

Como vimos anteriormente,  jqpuede extraer valores de datos que se canalizan desde JSON. También puede funcionar con JSON almacenado en un archivo. Vamos a trabajar con archivos locales para que la línea de comandos no esté abarrotada de curlcomandos. Esto debería hacer que sea un poco más fácil de seguir.

La forma más sencilla de extraer datos de un archivo JSON es proporcionar un nombre de clave para obtener su valor de datos. Escriba un punto y el nombre de la clave sin espacios entre ellos. Esto crea un filtro a partir del nombre de la clave. También necesitamos decir jqqué archivo JSON usar.

Escribimos lo siguiente para recuperar el messagevalor:

jq .message iss.json

El comando "jq .message iss.json" en una ventana de terminal.

jqimprime el texto del message valor en la ventana de terminal.

Si tiene un nombre de clave que incluye espacios o puntuación, debe envolver su filtro entre comillas. Por lo general, se tiene cuidado de usar caracteres, números y guiones bajos solo para que los nombres de las claves JSON no sean problemáticos.

Primero, escribimos lo siguiente para recuperar el timestampvalor:

jq .timestamp iss.json

El comando "jq .timestamp iss.json" en una ventana de terminal.

El valor de la marca de tiempo se recupera e imprime en la ventana del terminal.

Pero, ¿cómo podemos acceder a los valores dentro del  iss_positionobjeto? Podemos usar la notación de puntos JSON. Incluiremos el iss_positionnombre del objeto en la «ruta» al valor de la clave. Para hacer esto, el nombre del objeto en el que se encuentra la clave precederá al nombre de la clave.

Escribimos lo siguiente, incluido el latitudenombre de la clave (tenga en cuenta que no hay espacios entre «.iss_position» y «.latitude»):

jq .iss_position.latitude iss.json

El comando "jq .iss_position.latitude iss.json" en una ventana de terminal.

Para extraer varios valores, debe hacer lo siguiente:

  • Enumere los nombres de las teclas en la línea de comando.
  • Sepárelos con comas ( ,).
  • Escríbalos entre comillas ( ") o apóstrofos ( ').

Con eso en mente, escribimos lo siguiente:

jq ".iss_position.latitude, .timestamp" iss.json

El comando "jq" .iss_position.latitude, .timestamp "iss.json" en una ventana de terminal.

Los dos valores se imprimen en la ventana del terminal.

Trabajar con matrices

Tomemos un objeto JSON diferente de la NASA.

Esta vez, usaremos una lista de los astronautas que están en el espacio ahora mismo :

curl -s http://api.open-notify.org/astros.json

El comando "curl -s http://api.open-notify.org/astros.json" en una ventana de terminal.

Bien, funcionó, hagámoslo de nuevo.

jqEscribiremos lo siguiente para canalizarlo y redirigirlo a un archivo llamado «astro.json»:

curl -s http://api.open-notify.org/astros.json | jq. > astro.json

El comando "curl -s http://api.open-notify.org/astros.json | jq.> Astros.json" en una ventana de terminal.

Ahora escriba lo siguiente para verificar nuestro archivo:

menos astro.json

El comando "less astros.json" en una ventana de terminal.

Como se muestra a continuación, ahora vemos la lista de astronautas en el espacio, así como sus naves espaciales.

Salida de "less astros.json" en una ventana de terminal.

Este objeto JSON contiene una matriz llamada people. Sabemos que es una matriz debido al corchete de apertura ( [) (resaltado en la captura de pantalla anterior). Es una matriz de objetos que cada uno contiene dos pares clave: valor:   namey craft.

Como hicimos anteriormente, podemos usar la notación de puntos JSON para acceder a los valores. También debemos incluir los corchetes ( []) en el nombre de la matriz.

Con todo eso en mente, escribimos lo siguiente:

jq ".personas []. nombre" astro.json

El comando "jq" .people []. Name "astros.json" en una ventana de terminal.

Esta vez, todos los valores de nombre se imprimen en la ventana de la terminal. Lo que pedimos jqhacer fue imprimir el valor del nombre para cada objeto en la matriz. Bastante ordenado, ¿eh?

Podemos recuperar el nombre de un solo objeto si ponemos su posición en la matriz entre corchetes ( []) en la línea de comando. La matriz utiliza indexación de compensación cero , lo que significa que el objeto en la primera posición de la matriz es cero.

Para acceder al último objeto de la matriz, puede utilizar -1; para obtener el penúltimo objeto de la matriz, puede usar -2, y así sucesivamente.

A veces, el objeto JSON proporciona la cantidad de elementos en la matriz, que es el caso de este. Junto con la matriz, contiene un par clave: nombre llamado numbercon un valor de seis.

Relacionado:  ¿En qué se diferencia Crosh de la terminal de Linux en un Chromebook?

El siguiente número de objetos están en esta matriz:

jq ".people [1] .name" astro.json
jq ".people [3] .name" astro.json
jq ".people [-1] .name" astro.json
jq ".people [-2] .name" astro.json

El "jq" .people [1] .name "astro.json", "jq" .people [3] .name "astro.json", "jq" .people [-1] .name "astro.json," y "jq" .people [-2] .name "astro.json" en una ventana de terminal.

También puede proporcionar un objeto inicial y final dentro de la matriz. Esto se llama «rebanar» y puede resultar un poco confuso. Recuerde que la matriz usa un desplazamiento cero.

Para recuperar los objetos desde la posición de índice dos, hasta (pero sin incluir) el objeto en la posición de índice cuatro, escribimos el siguiente comando:

jq ".personas [2: 4]" astro.json

El comando "jq" .people [2: 4] "astro.json" en una ventana de terminal.

Esto imprime los objetos en el índice de matriz dos (el tercer objeto de la matriz) y tres (el cuarto objeto de la matriz). Detiene el procesamiento en el índice cuatro de la matriz, que es el quinto objeto de la matriz.

La forma de entender mejor esto es experimentar en la línea de comandos. Pronto verás cómo funciona.

Cómo usar tuberías con filtros

Puede canalizar la salida de un filtro a otro y no es necesario que aprenda un nuevo símbolo. Al igual que la línea de comandos de Linux,  jqusa la barra vertical ( |) para representar una tubería.

Le diremos  jqque canalice la peoplematriz al .namefiltro, que debería enumerar los nombres de los astronautas en la ventana de la terminal.

Escribimos lo siguiente:

jq ".people [] | .name" astro.json

La gente "jq" [] |  .name "astros.json" comando en una ventana de terminal.

Crear matrices y modificar resultados

Podemos utilizar jqpara crear nuevos objetos, como matrices. En este ejemplo, extraeremos tres valores y crearemos una nueva matriz que contenga esos valores. Tenga en cuenta que los [corchetes de apertura ( ) y de cierre ( ]) también son el primer y último carácter de la cadena de filtro.

Escribimos lo siguiente:

jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json

El comando "jq" [.iss-position.latitude, iss_position.longitude, .timestamp] "iss.json" en una ventana de terminal.

La salida se incluye entre corchetes y se separa por comas, lo que la convierte en una matriz formada correctamente.

Los valores numéricos también se pueden manipular a medida que se recuperan. Extraigamos el timestampdel archivo de posición de ISS y luego extraigamos de nuevo y cambiemos el valor que se devuelve.

Para hacerlo, escribimos lo siguiente:

jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json

Los comandos "jq" .timestamp "iss.json" y "jq" .timestamp - 1570000000 "iss.json" en una ventana de terminal.

Esto es útil si necesita agregar o eliminar un desplazamiento estándar de una matriz de valores.

Escribamos lo siguiente para recordarnos qué iss.jsoncontiene el archivo:

jq. iss.json

El comando "jq. Iss.json" en una ventana de terminal.

Digamos que queremos deshacernos del messagepar clave: valor. No tiene nada que ver con la posición de la Estación Espacial Internacional. Es solo una bandera que indica que la ubicación se recuperó correctamente. Si excede los requisitos, podemos prescindir de él. (También puede simplemente ignorarlo).

Podemos usar jqla función de borrar  del(), para borrar un par clave: valor. Para eliminar el mensaje clave: par de valores, escribimos este comando:

jq "del (.message)" iss.json

El comando "jq" del (.message) "iss.json" en una ventana de terminal.

Tenga en cuenta que esto en realidad no lo elimina del archivo «iss.json»; simplemente lo elimina de la salida del comando. Si necesita crear un nuevo archivo sin el messagepar clave: valor en él, ejecute el comando y luego redirija la salida a un nuevo archivo.

Objetos JSON más complicados

Recuperemos más datos de la NASA. Esta vez, usaremos un objeto JSON que contiene información sobre sitios de impacto de meteoritos de todo el mundo. Este es un archivo más grande con una estructura JSON mucho más complicada que los que hemos tratado anteriormente.

Primero, escribiremos lo siguiente para redirigirlo a un archivo llamado «strikes.json»:

curl -s https://data.nasa.gov/resource/y77d-th95.json | jq. > strikes.json

El comando "curl -s https://data.nasa.gov/resource/y77d-th95.json | jq.> Strikes.json" en una ventana de terminal.

Para ver cómo se ve JSON, escribimos lo siguiente:

menos strikes.json

El comando "less strikes.json" en less en una ventana de terminal.

Como se muestra a continuación, el archivo comienza con un corchete de apertura ( [), por lo que todo el objeto es una matriz. Los objetos de la matriz son colecciones de pares clave: valor, y hay un objeto anidado llamado geolocation. El geolocationobjeto contiene más pares clave: valor y una matriz llamada coordinates.

Salida del comando "less strikes.json" en less en una ventana de terminal.

Recuperemos los nombres de los impactos de meteoritos del objeto en la posición del índice 995 hasta el final de la matriz.

Escribiremos lo siguiente para canalizar el JSON a través de tres filtros:

jq ". [995:] |. [] | .name" strikes.json

El "jq". [995:] |  . [] |  .name comando "strikes.json" en una ventana de terminal.

Los filtros funcionan de las siguientes formas:

  • .[995:]: Esto le dice jqque procese los objetos desde el índice de matriz 995 hasta el final de la matriz. Ningún número después de los dos puntos ( :) es lo que indica  jqque se continúe hasta el final de la matriz.
  • .[]: Este iterador de matriz indica jqque se procese cada objeto de la matriz.
  • .name: Este filtro extrae el valor del nombre.
Relacionado:  Cómo verificar el uso de memoria desde la terminal de Linux

Con un ligero cambio, podemos extraer los últimos 10 objetos de la matriz. Un «-10» indica jq que se comience a procesar los objetos 10 desde el final de la matriz.

Escribimos lo siguiente:

jq ". [- 10:] |. [] | .name" strikes.json

El "jq". [- 10:] |  . [] |  .name comando "strikes.json" en una ventana de terminal.

Al igual que en los ejemplos anteriores, podemos escribir lo siguiente para seleccionar un solo objeto:

jq ". [650] .name" strikes.json

El comando "jq". [650] .name "strikes.json" en una ventana de terminal.

También podemos aplicar el corte a las cuerdas. Para hacerlo, escribiremos lo siguiente para solicitar los primeros cuatro caracteres del nombre del objeto en el índice de matriz 234:

jq ". [234] .name [0: 4]" strikes.json

El comando "jq". [234] .name [0: 4] "strikes.json" en una ventana de terminal.

También podemos ver un objeto específico en su totalidad. Para hacer esto, escribimos lo siguiente e incluimos un índice de matriz sin ninguna clave: filtros de valor:

jq ". [234]" strikes.json

El comando "jq". [234] "strikes.json" en una ventana de terminal.

Si desea ver solo los valores, puede hacer lo mismo sin los nombres de las claves.

Para nuestro ejemplo, escribimos este comando:

jq ". [234] []" strikes.json

El comando "jq" [234] [] "strikes.json" en una ventana de terminal.

Para recuperar varios valores de cada objeto, los separamos con comas en el siguiente comando:

jq ". [450: 455] |. [] | .name, .mass" strikes.json

El "jq". [450: 455] |  . [] |  .name, .mass comando "strikes.json" en una ventana de terminal.

Si desea recuperar valores anidados, debe identificar los objetos que forman la «ruta» hacia ellos.

Por ejemplo, para hacer referencia a los coordinatesvalores, tenemos que incluir la matriz que lo abarca todo, el geolocationobjeto anidado y la coordinatesmatriz anidada , como se muestra a continuación.

La ruta a la matriz en un objeto JSON anidado resaltado en una ventana de terminal.

Para ver los coordinatesvalores del objeto en la posición de índice 121 de la matriz, escribimos el siguiente comando:

jq ". [121] .geolocation.coordinates []" strikes.json

El comando "jq". [121] .geolocation.coordinates [] "strikes.json" en una ventana de terminal.

La función de longitud

La jq lengthfunción da distintas métricas según lo que se haya aplicado, como por ejemplo:

  • Cadenas : la longitud de la cadena en bytes.
  • Objetos : el número de pares clave: valor en el objeto.
  • Matrices : el número de elementos de matriz en la matriz.

El siguiente comando devuelve la longitud del namevalor en 10 de los objetos de la matriz JSON, comenzando en la posición del índice 100:

jq ". [100: 110] |. []. nombre | longitud" strikes.json

El "jq". [100: 110] |  . []. nombre |  length "strikes.json" comando en una ventana de terminal.

Para ver cuántos pares clave: valor hay en el primer objeto de la matriz, escribimos este comando:

jq ". [0] | longitud" strikes.json

El "jq". [0]  |  length "strikes.json" comando en una ventana de terminal.

Las teclas Función

Puede usar la función de teclas para averiguar sobre el JSON con el que debe trabajar. Puede decirle cuáles son los nombres de las claves y cuántos objetos hay en una matriz.

Para encontrar las claves en el peopleobjeto en el archivo «astro.json», escribimos este comando:

jq ".personas. [0] | claves" astro.json

La gente "jq". [0]  |  teclas comando "astro.json" en una ventana de terminal.

Para ver cuántos elementos hay en la peoplematriz, escribimos este comando:

jq ".people | keys" astro.json

La gente "jq" |  teclas comando "astro.json" en una ventana de terminal.

Esto muestra que hay seis elementos de matriz de compensación cero, numerados del cero al cinco.

La función has ()

Puede usar la has()función para interrogar al JSON y ver si un objeto tiene un nombre de clave en particular. Tenga en cuenta que el nombre de la clave debe estar entre comillas. Envolveremos el comando de filtro entre comillas simples ( '), de la siguiente manera:

jq '. [] | tiene ("nametype") 'strikes.json

El "jq '. [] | Tiene (" nametype ")' strikes.json" en una ventana de terminal.

Cada objeto de la matriz está marcado, como se muestra a continuación.

Salida del comando "jq '. [] | Has (" nametype ")' strikes.json" en una ventana de terminal.

Si desea verificar un objeto específico, incluya su posición de índice en el filtro de matriz, de la siguiente manera:

jq '. [678] | tiene ("nametype") 'strikes.json

El comando "jq '. [678] | has (" nametype ")' strikes.json" en una ventana de terminal.

No te acerques a JSON sin él

La jqutilidad es el ejemplo perfecto del software profesional, potente y rápido que hace que vivir en el mundo Linux sea un placer.

Esta fue solo una breve introducción a las funciones comunes de este comando; hay mucho más. Asegúrese de consultar el manual completo de jq  si desea profundizar más.