Wie kann man JSON mit Shell-Scripting unter Linux analysieren?

56

Ich habe eine JSON-Ausgabe, aus der ich unter Linux einige Parameter extrahieren muss.

Dies ist die JSON-Ausgabe:

{
        "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
            }
        ]
    }

Ich möchte eine Datei schreiben, die Überschriften wie Instanz-ID, Tag wie Name, Kostenstelle, Eigentümer enthält. und darunter bestimmte Werte aus der JSON-Ausgabe. Die hier angegebene Ausgabe ist nur ein Beispiel.

Wie kann ich das mit sedund machen awk?

Erwartete Ausgabe :

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
user3086014
quelle
1
Leiten Sie Ihren CLI-Aufruf an Python weiter, da er für EC2-Instanzen typisch ist. Python kann JSON leicht interpretieren. In der Antwort unten finden Sie ein Beispiel. Natürlich können Sie auch eine andere SS-Sprache verwenden, für diese ist jedoch eine Installation erforderlich, während Python bereits vorhanden ist.
Robbie Averill
Wie wäre es mit Knoten ?
Eliran Malka

Antworten:

65

Die Verfügbarkeit von Parsern in nahezu jeder Programmiersprache ist einer der Vorteile von JSON als Datenaustauschformat.

Anstatt zu versuchen, einen JSON-Parser zu implementieren, ist es wahrscheinlich besser, ein für JSON-Parsing entwickeltes Tool wie jq oder eine Allzweck-Skriptsprache mit einer JSON-Bibliothek zu verwenden.

Mit jq können Sie beispielsweise die ImageID wie folgt aus dem ersten Element des Instances-Arrays ziehen:

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

Alternativ können Sie dieselben Informationen auch mit Rubys JSON-Bibliothek abrufen:

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

Ich werde nicht alle Ihre überarbeiteten Fragen und Kommentare beantworten, aber das Folgende ist hoffentlich genug, um Ihnen den Einstieg zu erleichtern.

Angenommen, Sie hätten ein Ruby-Skript, das ein aus STDIN lesen und die zweite Zeile in Ihrer Beispielausgabe [0] ausgeben könnte. Das Skript könnte ungefähr so ​​aussehen:

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

Wie könnten Sie ein solches Skript verwenden, um Ihr gesamtes Ziel zu erreichen? Angenommen, Sie hatten bereits Folgendes:

  • ein Befehl zum Auflisten aller Ihrer Instanzen
  • einen Befehl, um den obigen json für eine Instanz auf Ihrer Liste abzurufen und an STDOU auszugeben

Eine Möglichkeit wäre, Ihre Shell zu verwenden, um diese Werkzeuge zu kombinieren:

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

Vielleicht haben Sie jetzt einen einzigen Befehl, der Ihnen einen JSON-Blob für alle Instanzen mit mehr Elementen in diesem "Instances" -Array gibt. Wenn dies der Fall ist, müssen Sie das Skript nur ein wenig ändern, um das Array zu durchlaufen, anstatt einfach das erste Element zu verwenden.

Am Ende ist der Weg, um dieses Problem zu lösen, der Weg, um viele Probleme in Unix zu lösen. Teilen Sie es in leichtere Probleme auf. Finden oder schreiben Sie Tools, um das einfachere Problem zu lösen. Kombinieren Sie diese Tools mit Ihrer Shell oder anderen Betriebssystemfunktionen.

[0] Beachte, dass ich keine Ahnung habe, woher du die Kostenstelle nimmst, also habe ich es einfach erfunden.

Steven D
quelle
Ich habe JQ auf meinem Computer installiert. Aber ich weiß nicht, wie ich die Informationen bekommen soll. Ich aktualisiere die Frage
user3086014
Wie geht das. Der Befehl ec2-describe instance gibt ein solches reslut aus. Dies sind Daten für 1 Instanz, es gibt 100 Instanzen. Wie man das in einem Skript macht
user3086014
Ich habe AWS CLI-Tools, die mir die Ausgabe gibt. Jetzt, wie man die Ausgabe und die erforderlichen Tags analysiert, die ich wirklich nicht kenne
user3086014
2
@ user3086014 Es tut mir leid, aber ich werde nicht mehr Arbeit in diese Antwort stecken. Schauen Sie sich das Ruby-Beispiel an, das ich dort habe. Hier sollten Sie einen guten Einstieg in das Abrufen von Tags aus verschiedenen Teilen des gewünschten JSON finden.
Steven D
In der Vielzahl der verfügbaren JSON-Tools ist JQ mein Favorit. Stedolan.github.io/jq/manual . Erhältlich auch im Standardvertrieb. Ein Spielplatz für die Prüfung Filter ist verfügbar unter jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz
15

Sie können das folgende Python-Skript verwenden, um diese Daten zu analysieren. Nehmen wir an , dass Sie JSON - Daten aus Arrays in Dateien wie haben array1.json, array2.jsonund so weiter.

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

Und dann einfach laufen:

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

Ich habe keine Kosten in Ihren Daten gesehen, deshalb habe ich das nicht berücksichtigt.

Laut Diskussion in Kommentaren habe ich das Skript parse.py aktualisiert:

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"] 

Sie können versuchen, folgenden Befehl auszuführen:

#ec2-describe-instance <instance> | python parse.py
Robert Jonczy
quelle
Dies ist jedoch nur ein Array. Es gibt ähnliche Arrays, die vom Befehl zurückgegeben werden. wie das geht
user3086014
Diese Daten werden zur Laufzeit durch den ec2-describe-Instanzbefehl generiert. wie man damit
umgeht
Ich habe dieses Python-Skript ein wenig modifiziert: 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() Wenn Sie alle json-Daten von Arrays in Dateien wie array1.json, array2.json usw. haben, können Sie versuchen, es so # for x in auszuführen : ls * .json; do python parse.py $x; done
Robert Jonczy
Sie können die Antwort selbst aktualisieren. Es ist nicht lesbar
user3086014
Ich habe auch Arrays.100 von Arrays wie
folgt
9

Der folgende JQ-Code:

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

verwendet wie:

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

würde ausgeben:

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

Ein paar Hinweise zum Verständnis des Codes:

  • from_entriesNimmt ein Array von Objekten wie {key:a, value:b}und verwandelt es in ein Objekt mit entsprechenden Schlüssel / Wert-Paaren ( {a: b});
  • Die Tasten Keyund Valueim TagsArray mussten in Kleinbuchstaben umgewandelt werden.
  • Die letzte Zeichenfolge verwendet die Zeichenfolgeninterpolationsfunktion von jq. Sie können es nach Bedarf anpassen.

Weitere Informationen finden Sie im jq-Tutorial und im Handbuch unter https://stedolan.github.io/jq/.

Nadrieril
quelle
1
Sie können jetzt das Extrahieren von Tags mit verkürzen (.Tags | map({Value, Key}) | from_entries) as $tags, ohne die Schlüssel in Kleinbuchstaben umzuwandeln.
mloughran
8

Andere haben allgemeine Antworten auf Ihre Frage gegeben, die gute Möglichkeiten zum Parsen von json aufzeigen. Ich suchte jedoch wie Sie nach einer Möglichkeit, eine aws-Instanz-ID mit einem Kerntool wie awk oder sed zu extrahieren, ohne von anderen Paketen abhängig zu sein. Um dies zu erreichen, können Sie das Argument "--output = text" an Ihren aws-Befehl übergeben, wodurch Sie eine parsable awk-Zeichenfolge erhalten. Damit können Sie einfach die Instanz-ID mit etwas wie dem folgenden erhalten ...

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

Jshon ist in verschiedenen Distributionen erhältlich:

$ 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

Schlechte Erklärung: -e uuObjekt extrahieren uu, -aArray verwendbar machen (nicht sicher, ob ich dieses richtig formuliert habe, aber trotzdem ...), -uString dekodieren, -pzum vorherigen Element zurückkehren ( -i NN ist eine beliebige Zahl, scheint den gleichen Effekt zu haben) .

Abhängig von Ihrem Fall kann die Ausgabe eine Nachbehandlung erfordern (wie Sie sehen können).

Jshon scheint jedoch nicht robust gegen JSON-Missbildungen zu sein (Ihre "Tags" mit Kommas vor der schließenden geschweiften Klammer lösen einen Fehler aus).

Jemand hat jsawk in einem anderen Thread erwähnt, aber ich habe es nicht getestet.

Skippy le Grand Gourou
quelle
0

Hier ist ein Einzeiler-Vorschlag:

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)

Nicht perfekt, aber es würde funktionieren, wenn Sie es ein bisschen optimieren.

Grundsätzlich wird verwendet pr, um jedes eingestellte Ergebnis pro Spalte zu drucken. Jede Ergebnismenge wird durch eine Prozessersetzung zurückgegeben, die die JSON-Datei analysiert und Werte basierend auf dem Schlüssel zurückgibt.

Dies funktioniert ähnlich wie in: Wie kann ich bei gegebenem Schlüsselwert-Inhalt Werte nach Schlüssel gruppieren und nach Wert sortieren?

Kenorb
quelle
0

Schauen Sie sich jtccli tool an:

Mit dieser Funktion können Sie auf einfache Weise die erforderlichen Informationen aus Ihrem file.jsonJSON-Code extrahieren (vorausgesetzt , der JSON-Code ist in , übrigens, muss repariert werden, es gibt ein paar zusätzliche Kommas):

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.
quelle
-2

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

übersetzt Ihre Jason-Datei in etwas, das wie folgt gelesen werden kann:

{
  "Ausführung": [
    "sessionrestore",
    1
  ],
  "windows": [
    {
      "tabs": [
        {
          "Einträge": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "Zeichensatz": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "fortbestehen": wahr
            },
...

Nun sollte es möglich sein, Ihre Daten mit beliebigen Standardwerkzeugen zu analysieren

Ternitz
quelle