JSON analizando en el shell

11

¿Cómo puedo analizar la salida JSON en el shell?

Por ejemplo, Amazon Web Services proporciona una CLI para recuperar el estado de sus instancias:

$ aws ec2 describe-instances <my_instance_id>

Pero el comando devuelve una cadena JSON. La salida de ese comando se ve así:

$ aws ec2 describe-instances x12345
{
    "Reservations" :
     {  
            "OwnerId": "1345345"
            "Groups": [], 
            "SecurityGroups": [
               {
                  "Foo" : "yes"
                  "Bar" : "no
               }
             ]
     }
}

¿Hay incorporados shells que podrían usarse para analizar la salida JSON?

Por ejemplo, me gustaría capturar en una variable de shell FOO, lo siguiente output["Reservations"]["SecurityGroups"][0]{"Foo"}.

En caso de que ayude, estoy específicamente interesado en soluciones que puedan funcionar desde Zsh.

Amelio Vazquez-Reina
fuente
2
Normalmente usarías una herramienta, como jshon .
jasonwryan
1
Úselo --output textsi desea analizar en el shell sin utilizar herramientas externas como jshon.
jordanm
1
@jasonwryan - Recién escuchado jshonpor primera vez, seguí tu enlace. Después de leer eso, solo puedo decir que me fui muy contento de que, por casualidad, me enteré e instalé jqprimero. Creo que a usted también le gustaría escucharlo si aún no lo ha hecho, ya que no se molesta con todos esos interruptores de línea de comandos y puede hacer sus propias expresiones regulares, incluso le permite declarar funciones y variables si lo desea. Vea la respuesta aquí si está interesado.
mikeserv

Respuestas:

10

Según tengo entendido, estás buscando el valor de "Foo". Esto es realmente fácil de hacer con la herramienta de línea de comandos de shell jq. Es algo así como sedque implementa su propio tipo de lenguaje analizador. Dado tu ejemplo:

json='
{
    "Reservations" :
     {  
            "OwnerId" : "1345345",
            "Groups" :  [],
            "SecurityGroups" : [
               {
                  "Foo" : "yes",
                  "Bar" : "no"
               }
             ]
     }
}'

jqpuede ser yestan simple como:

printf %s "$json" |
jq '.[].SecurityGroups[0].Foo?'                                                

SALIDA

"yes"

Puede recorrer un hash de objetos o una lista de diccionario utilizando la .dotnotación, y las matrices indexadas se pueden indexar de manera más simple, con, como probablemente haya adivinado, índices numéricos entre corchetes. En el comando anterior, uso el formulario de índice vacío para indicar que quiero que se expandan todos los elementos iterables de ese nivel. Eso puede ser más fácil de entender de esta manera:

printf %s "$json" | jq '.[][]'

... que divide todos los valores para los elementos de segundo nivel en el hash y me consigue ...

"1345345"
[]
[
  {
    "Foo": "yes",
    "Bar": "no"
  }
]

Esto apenas rasca la superficie con respecto a jqlas capacidades de. Es una herramienta inmensamente poderosa para serializar datos en el shell, se compila en un solo binario ejecutable en el estilo clásico de Unix, es muy probable que esté disponible a través del administrador de paquetes para su distribución, y está muy bien documentado. Visite su página y gitcompruébelo usted mismo.

Por cierto, otra forma de abordar los datos en capas json, al menos para tener una idea de con qué está trabajando, podría ser ir hacia otro lado y usar la .dotnotación para dividir todos los valores en todos los niveles, como:

printf %s "$json" | jq '..'

{
  "Reservations": {
    "OwnerId": "1345345",
    "Groups": [],
    "SecurityGroups": [
      {
        "Foo": "yes",
        "Bar": "no"
      }
    ]
  }
}
{
  "OwnerId": "1345345",
  "Groups": [],
  "SecurityGroups": [
    {
      "Foo": "yes",
      "Bar": "no"
    }
  ]
}
"1345345"
[]
[
  {
    "Foo": "yes",
    "Bar": "no"
  }
]
{
  "Foo": "yes",
  "Bar": "no"
}
"yes"
"no"

Pero mucho mejor, probablemente, sería usar uno de los muchos métodos de descubrimiento o búsqueda que jqofrece los distintos tipos de nodos.

mikeserv
fuente
10

Esta es una respuesta a su objetivo, pero no su pregunta. Lo que significa que puede lograr su objetivo sin usar un analizador JSON.

AWS cli util tiene la capacidad de generar solo campos de selección utilizando el --queryargumento. Esto está documentado aquí .

Por ejemplo:

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupName' \
  --instance-id i-6b272337 \
  --output text
mongodb

Incluso puede seleccionar múltiples campos si desea:

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[0].[GroupName,GroupId]' \
  --instance-id i-6b272337 \
  --output text
mongodb sg-710ffa14

Y también puede mostrar varias estructuras coincidentes:

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[*].[GroupName,GroupId]' \
  --instance-id i-6b272337 \
  --output text
mongodb sg-710ffa14
default sg-a0243bcc
Patricio
fuente
1
¡Gracias! Eso es muy útil. Estoy aceptando @mikeserv principalmente para servir a la comunidad con la respuesta a la pregunta, pero su respuesta es la que
usaré
¡Esto es muy útil! ¡¡Muchas gracias!!
MD Sayem Ahmed