Comment analyser JSON avec un script shell sous Linux?

56

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 qui contient en-tête comme instance ID, balise comme nom, centre de coût, 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 sedet awk?

Production attendue :

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
utilisateur3086014
la source
1
Dirigez votre appel CLI vers Python, car il est natif des instances EC2. Python peut facilement interpréter JSON. Voir la réponse ci-dessous pour un exemple. Bien sûr, vous pouvez également utiliser n’importe quel autre langage SS, mais ils nécessiteront une installation alors que Python est déjà présent.
Robbie Averill
comment utiliser noeud ?
Eliran Malka

Réponses:

65

La disponibilité d'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, il vaut probablement mieux utiliser un outil conçu pour l'analyse JSON, tel que jq, ou un langage de script à usage général doté d'une bibliothèque JSON.

Par exemple, à l'aide de jq, vous pouvez extraire l'imageID du premier élément du tableau Instances, comme suit:

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

Sinon, pour obtenir les mêmes informations à l'aide de 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 les éléments suivants sont, espérons-le, suffisants pour vous aider à démarrer.

Supposons que vous disposiez d'un script Ruby capable de lire a partir de STDIN et d'afficher la deuxième ligne de 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 votre objectif? Eh bien, supposons que vous ayez déjà eu ce qui suit:

  • 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 solution consiste à 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, vous avez peut-être 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 tel est le cas, il vous suffira de modifier un peu le script pour parcourir le tableau plutôt que d'utiliser simplement le premier élément.

En fin de compte, le moyen de résoudre ce problème est le moyen 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 ne sais pas d'où vous tirez le centre de coûts, je l'ai donc inventé.

Steven D
la source
j'ai installé jq sur ma machine. mais je ne sais pas comment obtenir l'information. je mets à jour la question
user3086014
Comment faire ça. la commande ec2-describe instance donne reslut comme ceci. ce sont des données pour 1 instance, il y a 100 instances. Comment faire cela dans un script
user3086014 Le
j'ai aws cli tools qui me donne la sortie. maintenant comment analyser la sortie et les balises requises que je ne sais vraiment pas
user3086014
2
@ user3086014 Je suis désolé, mais je ne mettrai pas plus de travail dans cette réponse. Regardez l'exemple de Ruby que j'ai là-bas. Cela devrait vous donner un bon point de départ pour savoir comment extraire des balises de différentes parties du JSON que vous souhaitez.
Steven D
Dans la moltitude des outils json disponibles jq est mon préféré stedolan.github.io/jq/manual . Disponible également dans la distribution std. Un terrain de jeu pour tester les filtres est disponible à l' adresse jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz le
15

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

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 juste courir:

$ 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 n'ai pas inclus cela.

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
Robert Jonczy
la source
mais ce n'est qu'un tableau, il y a des tableaux similaires retournés par la commande. comment faire cela
user3086014
et ces données sont générées par la commande ec2-describe instance au moment de l'exécution. comment gérer cela
user3086014
J'ai un peu modifié ce script python: import json from pprint import pprint jdata = open('example.json') 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() si vous avez toutes les données json de tableaux dans des fichiers tels que array1.json, array2.json, ... et ainsi de suite, vous pouvez essayer de l'exécuter comme ceci: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy
vous pouvez mettre à jour la réponse elle-même. ce n'est pas lisible
utilisateur3086014
J'ai aussi des tableaux. 100 de tableaux comme celui-ci
user3086014
9

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 indications pour comprendre le code:

  • from_entriesprend 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 Keyet Valuedu Tagstableau devaient ê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, voir le tutoriel et le manuel de jq à l’ adresse https://stedolan.github.io/jq/

Nadrieril
la source
1
Vous pouvez maintenant raccourcir l'extraction des balises en utilisant (.Tags | map({Value, Key}) | from_entries) as $tags, sans convertir les clés en minuscules.
mloughran
8

D'autres ont fourni des réponses générales à votre question qui démontrent de bonnes façons d'analyser json. Cependant, comme vous, je cherchais un moyen d'extraire un identifiant d'instance aws à l'aide d'un outil fondamental comme awk ou sed sans dépendre d'autres packages. Pour ce faire, vous pouvez passer l'argument "--output = text" à votre commande aws, ce 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}'
Mick Giles
la source
3

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 uuextraira l'objet uu, -arendra le tableau utilisable (je ne suis pas sûr que j'ai bien formulé celui-ci mais de toute façon…), -udécodera la chaîne, -previendra à l'élément précédent (semble que -i N, N étant un nombre quelconque, a le même effet) .

En fonction de votre cas, la sortie peut nécessiter un post-traitement (comme le vôtre, comme vous pouvez le constater).

Jshon doesn semble robuste contre les malformations JSON, cependant (vos "balises" avec des virgules avant la parenthèse fermante déclencheront une erreur).

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

Skippy le Grand Gourou
la source
0

Voici une suggestion one-liner:

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 modifiiez un peu.

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

Cela fonctionne comme décrit dans: Compte tenu du contenu clé-valeur, comment regrouper les valeurs par clé et les trier par valeur?

Kenorb
la source
0

Jetez un coup d'œil à l' jtcoutil cli:

cela 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 en plus):

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 $ 
Dmitry L.
la source
-2

jq "." recovery.js | head -n 20

traduit votre fichier jason en quelque chose de lisible comme ceci:

{
  "version": [
    "sessionrestore",
    1
  ],
  "les fenêtres": [
    {
      "onglets": [
        {
          "entrées": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "jeu de caractères": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "persister": vrai
            },
...

Maintenant, il devrait être possible d'analyser vos données avec n'importe quel outil standard

Ternitz
la source