web-dev-qa-db-fra.com

Comment analyser JSON avec des scripts Shell sous Linux?

J'ai une sortie JSON à partir de laquelle j'ai besoin d'extraire quelques paramètres sous Linux.

Voici la sortie JSON:

{
        "OwnerId": "121456789127",
        "ReservationId": "r-48465168",
        "Groups": [],
        "Instances": [
            {
                "Monitoring": {
                    "State": "disabled"
                },
                "PublicDnsName": null,
                "RootDeviceType": "ebs",
                "State": {
                    "Code": 16,
                    "Name": "running"
                },
                "EbsOptimized": false,
                "LaunchTime": "2014-03-19T09:16:56.000Z",
                "PrivateIpAddress": "10.250.171.248",
                "ProductCodes": [
                    {
                        "ProductCodeId": "aacglxeowvn5hy8sznltowyqe",
                        "ProductCodeType": "marketplace"
                    }
                ],
                "VpcId": "vpc-86bab0e4",
                "StateTransitionReason": null,
                "InstanceId": "i-1234576",
                "ImageId": "AMI-b7f6c5de",
                "PrivateDnsName": "ip-10-120-134-248.ec2.internal",
                "KeyName": "Test_Virginia",
                "SecurityGroups": [
                    {
                        "GroupName": "Test",
                        "GroupId": "sg-12345b"
                    }
                ],
                "ClientToken": "VYeFw1395220615808",
                "SubnetId": "subnet-12345314",
                "InstanceType": "t1.micro",
                "NetworkInterfaces": [
                    {
                        "Status": "in-use",
                        "SourceDestCheck": true,
                        "VpcId": "vpc-123456e4",
                        "Description": "Primary network interface",
                        "NetworkInterfaceId": "eni-3619f31d",
                        "PrivateIpAddresses": [
                            {
                                "Primary": true,
                                "PrivateIpAddress": "10.120.134.248"
                            }
                        ],
                        "Attachment": {
                            "Status": "attached",
                            "DeviceIndex": 0,
                            "DeleteOnTermination": true,
                            "AttachmentId": "eni-attach-9210dee8",
                            "AttachTime": "2014-03-19T09:16:56.000Z"
                        },
                        "Groups": [
                            {
                                "GroupName": "Test",
                                "GroupId": "sg-123456cb"
                            }
                        ],
                        "SubnetId": "subnet-31236514",
                        "OwnerId": "109030037527",
                        "PrivateIpAddress": "10.120.134.248"
                    }
                ],
                "SourceDestCheck": true,
                "Placement": {
                    "Tenancy": "default",
                    "GroupName": null,
                    "AvailabilityZone": "us-east-1c"
                },
                "Hypervisor": "xen",
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda",
                        "Ebs": {
                            "Status": "attached",
                            "DeleteOnTermination": false,
                            "VolumeId": "vol-37ff097b",
                            "AttachTime": "2014-03-19T09:17:00.000Z"
                        }
                    }
                ],
                "Architecture": "x86_64",
                "KernelId": "aki-88aa75e1",
                "RootDeviceName": "/dev/sda1",
                "VirtualizationType": "paravirtual",
                "Tags": [
                    {
                        "Value": "Server for testing RDS feature in us-east-1c AZ",
                        "Key": "Description"
                    },
                    {
                        "Value": "RDS_Machine (us-east-1c)",
                        "Key": "Name"
                    },
                    {
                        "Value": "1234",
                        "Key": "cost.centre",
                      },
                    {
                        "Value": "Jyoti Bhanot",
                        "Key": "Owner",
                      }
                ],
                "AmiLaunchIndex": 0
            }
        ]
    }

Je veux écrire un fichier contenant un en-tête comme l'ID d'instance, une balise comme le nom, le centre de coûts, le propriétaire. et en dessous de certaines valeurs de la sortie JSON. La sortie donnée ici n'est qu'un exemple.

Comment puis-je faire cela en utilisant sed et awk?

Sortie attendue:

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
64
user3086014

La disponibilité des analyseurs dans presque tous les langages de programmation est l'un des avantages de JSON en tant que format d'échange de données.

Plutôt que d'essayer d'implémenter un analyseur JSON, vous feriez probablement mieux d'utiliser soit un outil conçu pour l'analyse JSON tel que jq ou un langage de script à usage général qui possède une bibliothèque JSON.

Par exemple, en utilisant jq, vous pouvez extraire l'ImageID du premier élément du tableau Instances comme suit:

jq '.Instances[0].ImageId' test.json

Alternativement, pour obtenir les mêmes informations en utilisant la bibliothèque JSON de Ruby:

Ruby -rjson -e 'j = JSON.parse(File.read("test.json")); puts j["Instances"][0]["ImageId"]'

Je ne répondrai pas à toutes vos questions et commentaires révisés, mais nous espérons que ce qui suit est suffisant pour vous aider à démarrer.

Supposons que vous disposiez d'un script Ruby qui pourrait lire un depuis STDIN et afficher la deuxième ligne dans votre exemple de sortie [0]. Ce script pourrait ressembler à quelque chose comme:

#!/usr/bin/env Ruby
require 'json'

data = JSON.parse(ARGF.read)
instance_id = data["Instances"][0]["InstanceId"]
name = data["Instances"][0]["Tags"].find {|t| t["Key"] == "Name" }["Value"]
owner = data["Instances"][0]["Tags"].find {|t| t["Key"] == "Owner" }["Value"]
cost_center = data["Instances"][0]["SubnetId"].split("-")[1][0..3]
puts "#{instance_id}\t#{name}\t#{cost_center}\t#{owner}"

Comment pourriez-vous utiliser un tel script pour atteindre l'ensemble de votre objectif? Eh bien, supposons que vous disposiez déjà des éléments suivants:

  • une commande pour lister toutes vos instances
  • une commande pour obtenir le json ci-dessus pour n'importe quelle instance de votre liste et le sortir vers STDOU

Une façon serait d'utiliser votre Shell pour combiner ces outils:

echo -e "Instance id\tName\tcost centre\tOwner"
for instance in $(list-instances); do
    get-json-for-instance $instance | ./ugly-Ruby-scriptrb
done

Maintenant, peut-être que vous avez une seule commande qui vous donne un blob json pour toutes les instances avec plus d'éléments dans ce tableau "Instances". Eh bien, si c'est le cas, vous aurez juste besoin de modifier un peu le script pour parcourir le tableau plutôt que d'utiliser simplement le premier élément.

En fin de compte, la façon de résoudre ce problème est la façon de résoudre de nombreux problèmes sous Unix. Décomposez-le en problèmes plus faciles. Trouvez ou écrivez des outils pour résoudre le problème le plus facile. Combinez ces outils avec votre Shell ou d'autres fonctionnalités du système d'exploitation.

[0] Notez que je n'ai aucune idée d'où vous obtenez le centre de coûts, donc je viens de l'inventer.

72
Steven D

Vous pouvez utiliser le script python suivant pour analyser ces données. Supposons que vous disposez de données JSON provenant de tableaux dans des fichiers comme array1.json, array2.json etc.

import json
import sys
from pprint import pprint

jdata = open(sys.argv[1])

data = json.load(jdata)

print "InstanceId", " - ", "Name", " - ", "Owner"
print data["Instances"][0]["InstanceId"], " - " ,data["Instances"][0]["Tags"][1]["Value"], " - " ,data["Instances"][0]["Tags"][2]["Value"] 

jdata.close()

Et puis lancez:

$ for x in `ls *.json`; do python parse.py $x; done
InstanceId  -  Name  -  Owner
i-1234576  -  RDS_Machine (us-east-1c)  -  Jyoti Bhanot

Je n'ai pas vu le coût dans vos données, c'est pourquoi je ne l'ai pas inclus.

Selon la discussion dans les commentaires, j'ai mis à jour le script parse.py:

import json
import sys
from pprint import pprint

jdata = sys.stdin.read()

data = json.loads(jdata)

print "InstanceId", " - ", "Name", " - ", "Owner"
print data["Instances"][0]["InstanceId"], " - " ,data["Instances"][0]["Tags"][1]["Value"], " - " ,data["Instances"][0]["Tags"][2]["Value"] 

Vous pouvez essayer d'exécuter la commande suivante:

#ec2-describe-instance <instance> | python parse.py
15
Robert Jonczy

D'autres ont fourni des réponses générales à votre question qui démontrent de bonnes façons d'analyser json mais moi, comme vous, je cherchais un moyen d'extraire un identifiant d'instance aws en utilisant un outil de base comme awk ou sed sans dépendre d'autres packages. Pour ce faire, vous pouvez passer l'argument "--output = text" à votre commande aws qui vous donnera une chaîne analysable awk. Avec cela, vous pouvez simplement obtenir l'ID d'instance en utilisant quelque chose comme ce qui suit ...

aws ec2 run-instances --output text  | awk -F"\t" '$1=="INSTANCES" {print $8}'
9
Mick Giles

Le code jq suivant:

.Instances[] | (.Tags | map(.value=.Value | .key=.Key) | from_entries) as $tags | "\(.InstanceId) | \($tags.Name) | \($tags["cost.centre"]) | \($tags.Owner)"

utilisé comme:

json_producer | jq -r '<jq code...>'

produirait:

i-1234576 | RDS_Machine (us-east-1c) | 1234 | Jyoti Bhanot

Quelques conseils pour comprendre le code:

  • from_entries prend un tableau d'objets comme {key:a, value:b} et le transforme en objet avec les paires clé/valeur correspondantes ({a: b});
  • Les clés Key et Value du tableau Tags ont dû être converties en minuscules;
  • La dernière chaîne utilise la fonction d'interpolation de chaîne de jq. Vous pouvez le modifier au besoin.

Pour plus de détails, allez voir le tutoriel et le manuel de jq sur https://stedolan.github.io/jq/

9
Nadrieril

Jshon est disponible en plusieurs distributions:

$ echo your_JSON|jshon -e Instances -a -e InstanceId -u -p -e Tags -a -e Key -u -p -e Value -u
i-1234576
Description
Server for testing RDS feature in us-east-1c AZ
Name
RDS_Machine (us-east-1c)
cost.centre
1234
Owner
Jyoti Bhanot

Mauvaise explication: -e uu va extraire l'objet uu, -a rendra le tableau utilisable (je ne suis pas sûr d'avoir correctement formulé celui-ci mais de toute façon…), -u décodera la chaîne, -p reviendra à l'élément précédent (semble que -i N, N étant un nombre quelconque, a le même effet).

Selon votre cas, la sortie peut nécessiter un post-traitement (comme le vôtre, comme vous pouvez le voir).

Jshon ne semble pas robuste contre les malformations JSON, cependant (vos "Tags" avec des virgules avant le crochet bouclé fermera une erreur).

Quelqu'un a mentionné jsawk dans un autre fil, mais je ne l'ai pas testé.

3

Si cela se limite au cas d'utilisation AWS fourni ci-dessus, vous devez utiliser les indicateurs --query et --output pour votre appel API CLI

http://docs.aws.Amazon.com/cli/latest/userguide/controlling-output.html

2
mmmizani

Voici une suggestion pour une ligne:

pr -mt \
 <(grep -o ".*: .*," in.json | grep -iw InstanceId | cut -d: -f2) \
 <(grep -o ".*: .*," in.json | grep -iw Value      | cut -d: -f2) \
 <(grep -o ".*: .*," in.json | grep -iw Key        | cut -d: -f2)

Pas parfait, mais ça marcherait si vous le peaufiniez un peu.

Il utilise essentiellement pr pour imprimer chaque résultat d'ensemble par colonne. Chaque jeu de résultats est renvoyé par substitution de processus qui analyse le fichier JSON et renvoie des valeurs en fonction de la clé.

Cela fonctionne de la même manière que décrit dans: Étant donné le contenu de la valeur-clé, comment puis-je regrouper les valeurs par clé et trier par valeur?

0
kenorb

Jetez un oeil à jtc outil cli:

il permet d'extraire facilement les informations requises de votre json (en supposant que ce soit dans file.json, btw, votre JSON doit être corrigé, il y a quelques virgules supplémentaires):

bash $ cat file.json | jtc -x '<InstanceId>l+0[-1]' -y '[InstanceId]' -y "[Key]:<Name>[-1][Value]" -y "[Key]:<cost.centre>[-1][Value]" -y "[Key]:<Owner>[-1][Value]" | sed 's/"/\\"/g' | xargs -L4 echo
"i-1234576" "RDS_Machine (us-east-1c)" "1234" "Jyoti Bhanot"
bash $ 
0
Dmitry L.