Come analizzare JSON con shell scripting in Linux?


56

Ho un output JSON da cui ho bisogno di estrarre alcuni parametri in Linux.

Questo è l'output 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
            }
        ]
    }

Voglio scrivere un file che contiene intestazione come ID istanza, tag come nome, centro di costo, proprietario. e al di sotto di determinati valori dall'output JSON. L'output qui fornito è solo un esempio.

Come posso farlo usando sede awk?

Uscita prevista:

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

1
Inoltra la tua chiamata CLI in Python, suggerito perché è nativo delle istanze EC2. Python può facilmente interpretare JSON. Vedere la risposta di seguito per un esempio. Ovviamente, potresti usare anche qualsiasi altro linguaggio SS, ma richiederanno installazioni mentre Python è già lì.
Robbie Averill,

che ne dici di usare il nodo ?
Eliran Malka

Risposte:


65

La disponibilità di parser in quasi tutti i linguaggi di programmazione è uno dei vantaggi di JSON come formato di scambio dati.

Invece di provare a implementare un parser JSON, probabilmente stai meglio usando uno strumento creato per l'analisi JSON come jq o un linguaggio di script generico che ha una libreria JSON.

Ad esempio, usando jq, è possibile estrarre ImageID dal primo elemento dell'array Instances come segue:

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

In alternativa, per ottenere le stesse informazioni utilizzando la libreria JSON di Ruby:

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

Non risponderò a tutte le vostre domande e commenti riveduti, ma si spera quanto segue per iniziare.

Supponiamo di avere uno script Ruby in grado di leggere un da STDIN e di generare la seconda riga nell'output di esempio [0]. Quella sceneggiatura potrebbe assomigliare a:

#!/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}"

Come hai potuto usare una sceneggiatura simile per raggiungere tutto il tuo obiettivo? Bene, supponiamo che tu abbia già avuto il seguente:

  • un comando per elencare tutte le tue istanze
  • un comando per ottenere il json sopra per qualsiasi istanza nell'elenco e inviarlo a STDOU

Un modo sarebbe usare la tua shell per combinare questi strumenti:

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

Ora, forse hai un solo comando che ti dà un BLOB JSON per tutte le istanze con più elementi in quell'array "Istanze". Bene, in tal caso, dovrai solo modificare un po 'lo script per iterare l'array piuttosto che usare semplicemente il primo elemento.

Alla fine, il modo di risolvere questo problema è il modo di risolvere molti problemi in Unix. Suddividilo in problemi più facili. Trova o scrivi strumenti per risolvere il problema più semplice. Combina quegli strumenti con la tua shell o altre funzionalità del sistema operativo.

[0] Notare che non ho idea da dove ottenere il centro di costo, quindi l'ho appena inventato.


ho installato jq sul mio computer. ma non so come ottenere le informazioni. sto aggiornando la domanda
user3086014

Come farlo. il comando ec2-descrizione istanza restituisce in questo modo. si tratta di dati per 1 istanza, ci sono 100 istanze. Come farlo in uno script
user3086014

ho strumenti di aws cli che mi danno l'output. ora come analizzare l'output e i tag richiesti che davvero non conosco
user3086014

2
@ user3086014 Mi dispiace, ma non dedicherò più lavoro a questa risposta. Dai un'occhiata all'esempio di Ruby che ho lì. Dovrebbe darti un buon punto di partenza su come estrarre i tag da varie parti del JSON che desideri.
Steven D,

Nella moltitudine di strumenti json disponibili, jq è il mio stedolan.github.io/jq/manual preferito . Disponibile anche in distribuzione std. Un parco giochi per testare i filtri è disponibile su jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz,

15

È possibile utilizzare il seguente script Python per analizzare tali dati. Lascia supporre che si dispone di dati JSON da array in file come array1.json, array2.jsone così via.

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()

E poi esegui:

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

Non ho visto i costi nei tuoi dati, ecco perché non li ho inclusi.

Secondo la discussione nei commenti, ho aggiornato lo 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"] 

Puoi provare a eseguire il comando seguente:

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

ma questo è solo un array, ci sono array simili restituiti dal comando. come farlo
user3086014

e questi dati sono generati dal comando di istanza ec2-description in fase di esecuzione. come gestirlo
user3086014

Ho modificato un po 'questo script di 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() se hai tutti i dati json di array in file come array1.json, array2.json, ... e così via, potresti provare a eseguirlo in questo modo: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy,

puoi aggiornare la risposta stessa. non è leggibile
user3086014

anche io ho array.100 di array come questo
user3086014

9

Il seguente codice jq:

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

usato come:

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

produrrebbe:

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

Alcuni suggerimenti per capire il codice:

  • from_entriesaccetta un array di oggetti simili {key:a, value:b}e lo trasforma in un oggetto con corrispondenti coppie chiave / valore ( {a: b});
  • Le chiavi Keye nell'array dovevano essere convertite in minuscolo;ValueTags
  • L'ultima stringa utilizza la funzione di interpolazione delle stringhe di jq. Puoi modificarlo secondo necessità.

Per maggiori dettagli, consultare il tutorial e il manuale di jq su https://stedolan.github.io/jq/


1
Ora puoi abbreviare l'estrazione dei tag usando (.Tags | map({Value, Key}) | from_entries) as $tags, senza convertire le chiavi in ​​minuscolo.
mloughran,

8

Altri hanno fornito risposte generali alla tua domanda che dimostrano buoni modi di analizzare json, ma io, come te, stavo cercando un modo per estrarre un ID istanza aws usando uno strumento di base come awk o sed senza dipendere da altri pacchetti. Per fare ciò puoi passare l'argomento "--output = text" al tuo comando aws che ti darà una stringa analizzabile awk. Con ciò puoi semplicemente ottenere l'ID istanza usando qualcosa come il seguente ...

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

3

Jshon è disponibile in diverse distribuzioni:

$ 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

Scarsa spiegazione: -e uuestrarrà l'oggetto uu, -arenderà l'array utilizzabile (non sono sicuro di averlo correttamente formulato ma comunque ...), -udecodificherà la stringa, -ptornerà all'elemento precedente (sembra che -i N, essendo N qualsiasi numero, abbia lo stesso effetto) .

A seconda del caso, l'output potrebbe richiedere un po 'di post-trattamento (come il tuo, come puoi vedere).

Jshon non sembra robusto contro la malformazione di JSON, però (i tuoi "Tag" con virgole prima della parentesi graffa di chiusura genereranno un errore).

Qualcuno ha citato jsawk in un altro thread, ma non l'ho provato.



0

Ecco un suggerimento di una riga:

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)

Non perfetto, ma funzionerebbe se lo modificassi un po '.

Fondamentalmente utilizza prper stampare ogni risultato impostato per colonna. Ogni set di risultati viene restituito dalla sostituzione del processo che analizza il file JSON e restituisce i valori in base alla chiave.

Funziona in modo simile come descritto in: Dato il contenuto del valore-chiave, come posso raggruppare i valori per chiave e ordinare per valore?


0

Dai un'occhiata allo jtcstrumento cli:

consente di estrarre facilmente le informazioni richieste dal tuo JSON (supponendo che sia file.json, a proposito, il tuo JSON deve essere riparato, ci sono un paio di virgole extra lì):

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 $ 

-2

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

traduce il tuo file jason in qualcosa di leggibile come questo:

{
  "versione": [
    "Sessionrestore",
    1
  ],
  "finestre": [
    {
      "tabs": [
        {
          "inserimenti": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "charset": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "persistere": vero
            },
...

Ora dovrebbe essere possibile analizzare i dati con qualsiasi strumento standard

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.