Analyse JSON sur le shell

11

Comment analyser la sortie JSON sur le shell?

Par exemple, Amazon Web Services fournit une CLI pour récupérer l'état de vos instances:

$ aws ec2 describe-instances <my_instance_id>

Mais la commande renvoie une chaîne JSON. La sortie de cette commande ressemble à ceci:

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

Existe-t-il des commandes intégrées pouvant être utilisées pour analyser la sortie JSON?

Par exemple, je voudrais capturer dans une variable shell FOO, ce qui suit output["Reservations"]["SecurityGroups"][0]{"Foo"}.

Au cas où cela aiderait, je suis spécifiquement intéressé par les solutions qui pourraient fonctionner avec Zsh.

Amelio Vazquez-Reina
la source
2
Vous utiliseriez généralement un outil, comme jshon .
jasonwryan
1
Utilisez --output textsi vous souhaitez analyser dans le shell sans utiliser d'outils externes comme jshon.
jordanm
1
@jasonwryan - N'ayant entendu parler que jshonpour la première fois, j'ai suivi votre lien. Après avoir lu cela, je peux seulement dire que je suis parti très heureux d'avoir, par hasard, entendu parler et installé en jqpremier. Je pense que vous aimerez peut-être aussi en entendre parler si vous ne l'avez pas déjà fait - cela ne dérange pas avec tous ces commutateurs de ligne de commande et peut faire ses propres expressions régulières - cela vous permet même de déclarer des fonctions et des variables si vous le souhaitez. Voir la réponse ici à ce sujet si vous êtes intéressé.
mikeserv

Réponses:

10

Si je comprends bien, vous recherchez la valeur de "Foo". C'est vraiment facile à faire avec l'outil de ligne de commande shell jq. C'est quelque chose comme seden ce qu'il implémente son propre type de langage d'analyse. Compte tenu de votre exemple:

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

jqpeut être yesaussi simple que:

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

PRODUCTION

"yes"

Vous pouvez parcourir un hachage d'objet ou une liste de dictionnaires à l'aide de la .dotnotation, et les tableaux indexés peuvent être indexés plus simplement, avec, comme vous l'avez probablement deviné, des index numériques entre crochets. Dans la commande ci-dessus, j'utilise le formulaire d'index vide pour indiquer que je veux que tous les éléments itérables de ce niveau soient développés. Cela peut être plus facile à comprendre de cette façon:

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

... qui décompose toutes les valeurs pour les éléments de deuxième niveau dans le hachage et me donne ...

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

Cela raye à peine la surface en ce qui concerne jqles capacités de. C'est un outil extrêmement puissant pour sérialiser les données dans le shell, il se compile en un seul binaire exécutable dans le style Unix classique, il est très probablement disponible via le gestionnaire de paquets pour votre distribution, et il est très bien documenté. S'il vous plaît visitez son git-Page et de voir par vous - même.

Soit dit en passant, une autre façon d'aborder les données en couches json- au moins pour avoir une idée de ce avec quoi vous travaillez - pourrait être d'aller dans l'autre sens et d'utiliser la .dotnotation pour décomposer toutes les valeurs à tous les niveaux, comme:

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"

Mais bien mieux, probablement, serait simplement d'utiliser l'une des nombreuses méthodes de découverte ou de recherche jqproposées pour les différents types de nœuds.

mikeserv
la source
10

C'est une réponse à votre objectif, mais pas à votre question. Cela signifie que vous pouvez atteindre votre objectif sans utiliser un analyseur JSON.

L'utilitaire AWS cli a la capacité de générer uniquement des champs de sélection à l'aide de l' --queryargument. Ceci est documenté ici .

Par exemple:

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

Vous pouvez même sélectionner plusieurs champs si vous le souhaitez:

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

Et vous pouvez également afficher plusieurs structures correspondantes:

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[*].[GroupName,GroupId]' \
  --instance-id i-6b272337 \
  --output text
mongodb sg-710ffa14
default sg-a0243bcc
Patrick
la source
1
Merci! C'est très utile. J'accepte @mikeserv principalement pour servir la communauté avec la réponse à la question, mais votre réponse est celle que j'utiliserai
Amelio Vazquez-Reina
C'est très utile! Merci beaucoup!!
MD Sayem Ahmed