Jak parsować JSON za pomocą skryptów powłoki w systemie Linux?

56

Mam wyjście JSON, z którego muszę wyodrębnić kilka parametrów w systemie Linux.

To jest wyjście 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
            }
        ]
    }

Chcę napisać plik, który zawiera nagłówek taki jak identyfikator instancji, tag jak nazwa, centrum kosztów, właściciel. a poniżej niektórych wartości z wyjścia JSON. Dane wyjściowe podane tutaj są tylko przykładem.

Jak mogę to zrobić za pomocą sedi awk?

Oczekiwany wynik :

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
użytkownik3086014
źródło
1
Potokuj swoje wywołanie CLI w python, sugerowane, ponieważ jest rodzime dla instancji EC2. Python może łatwo interpretować JSON. Zobacz przykład poniżej. Oczywiście możesz użyć dowolnego innego języka SS, ale będą one wymagały instalacji, podczas gdy Python już tam jest.
Robbie Averill
co powiesz na użycie węzła ?
Eliran Malka

Odpowiedzi:

65

Dostępność parserów w prawie każdym języku programowania jest jedną z zalet JSON jako formatu wymiany danych.

Zamiast próbować zaimplementować parser JSON, prawdopodobnie lepiej jest użyć albo narzędzia zbudowanego dla parsowania JSON, takiego jak jq, albo języka skryptowego ogólnego przeznaczenia, który ma bibliotekę JSON.

Na przykład, używając jq, możesz wyciągnąć ImageID z pierwszego elementu tablicy Instances w następujący sposób:

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

Alternatywnie, aby uzyskać te same informacje za pomocą biblioteki JSON Ruby:

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

Nie odpowiem na wszystkie twoje poprawione pytania i komentarze, ale mam nadzieję, że poniższe informacje pomogą Ci zacząć.

Załóżmy, że masz skrypt Ruby, który może odczytać STDIN i wypisać drugi wiersz w twoim przykładowym wyniku [0]. Ten skrypt może wyglądać mniej więcej tak:

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

Jak możesz użyć takiego skryptu, aby osiągnąć cały swój cel? Załóżmy, że masz już następujące elementy:

  • polecenie, aby wyświetlić listę wszystkich instancji
  • polecenie, aby pobrać powyższy plik json dla dowolnej instancji na liście i wysłać go do STDOU

Jednym ze sposobów byłoby użycie powłoki do połączenia tych narzędzi:

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

Być może masz jedno polecenie, które daje jeden blok json dla wszystkich instancji z większą liczbą elementów w tablicy „Instancje”. Cóż, w takim przypadku wystarczy zmodyfikować skrypt, aby iterować tablicę, a nie po prostu użyć pierwszego elementu.

W końcu sposób na rozwiązanie tego problemu jest sposobem na rozwiązanie wielu problemów w Uniksie. Podziel go na łatwiejsze problemy. Znajdź lub napisz narzędzia, aby rozwiązać łatwiejszy problem. Połącz te narzędzia z powłoką lub innymi funkcjami systemu operacyjnego.

[0] Pamiętaj, że nie mam pojęcia, skąd bierzesz centrum kosztów, więc właśnie to wymyśliłem.

Steven D.
źródło
Zainstalowałem jq na moim komputerze. ale nie wiem, jak uzyskać informacje. aktualizuję pytanie
user3086014
Jak to zrobić. polecenie ec2-opisz wystąpienie daje reslut w ten sposób. to są dane dla 1 wystąpienia, jest 100 wystąpień. Jak to zrobić w skrypcie
3086014
Mam narzędzia aws cli, które dają mi wynik. teraz jak analizować dane wyjściowe i wymagane tagi, których tak naprawdę nie wiem
user3086014
2
@ user3086014 Przykro mi, ale nie będę wkładał więcej pracy w tę odpowiedź. Spójrz na przykład Rubiego, który tam mam. To powinno dać ci dobre miejsce do rozpoczęcia, w jaki sposób uzyskać tagi z różnych części JSON, które chcesz.
Steven D
W moltitude dostępnych narzędzi json jq jest moim ulubionym stedolan.github.io/jq/manual . Dostępne również w standardowej dystrybucji. Plac zabaw do testowania filtrów jest dostępny na stronie jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz
15

Możesz użyć następującego skryptu Pythona do parsowania tych danych. Załóżmy, że masz dane JSON z tablic w plikach, takich jak array1.json, array2.jsoni tak dalej.

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

A następnie po prostu uruchom:

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

Nie widziałem kosztów w twoich danych, dlatego tego nie uwzględniłem.

Zgodnie z dyskusją w komentarzach zaktualizowałem skrypt 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"] 

Możesz spróbować uruchomić następujące polecenie:

#ec2-describe-instance <instance> | python parse.py
Robert Jonczy
źródło
ale to tylko jedna tablica, w której komenda zwraca podobne tablice. jak to zrobić
3086014
i te dane są generowane przez komendę ec2-opisz wystąpienie w czasie wykonywania. jak sobie z tym poradzić
3086014
Zmodyfikowałem trochę ten skrypt Pythona: 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() jeśli masz wszystkie dane json z tablic w plikach takich jak array1.json, array2.json, ... i tak dalej, możesz spróbować uruchomić to w następujący sposób: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy
możesz zaktualizować samą odpowiedź. jego nieczytelny
user3086014
również mam tablice. 100 tablic takich jak ten
user3086014
9

Następujący kod jq:

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

używane jak:

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

wyprowadziłby:

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

Kilka wskazówek, aby zrozumieć kod:

  • from_entriespobiera tablicę obiektów jak {key:a, value:b}i zamienia ją w obiekt z odpowiednimi parami klucz / wartość ( {a: b});
  • Te Keyi Valueklucze w Tagstablicy musiały być przeliczone na małe;
  • Ostatni ciąg wykorzystuje funkcję interpolacji ciągu jq. Możesz go dostosować w razie potrzeby.

Aby uzyskać więcej informacji, zobacz samouczek i podręcznik jq na https://stedolan.github.io/jq/

Nadrieril
źródło
1
Możesz teraz skrócić wypakowywanie tagów za pomocą (.Tags | map({Value, Key}) | from_entries) as $tags, bez konwersji kluczy na małe litery.
mloughran
8

Inni udzielili ogólnych odpowiedzi na twoje pytanie, które pokazują dobre sposoby parsowania jsona, jednak ja, podobnie jak ty, szukałem sposobu na wyodrębnienie identyfikatora instancji aws za pomocą podstawowego narzędzia, takiego jak awk lub sed, bez zależności od innych pakietów. Aby to osiągnąć, możesz przekazać argument „--output = text” do komendy aws, która da ci łańcuch przetwarzany przez awk. Dzięki temu możesz po prostu uzyskać identyfikator instancji, używając czegoś takiego:

aws ec2 run-instances --output text  | awk -F"\t" '$1=="INSTANCES" {print $8}'
Mick Giles
źródło
3

Jshon jest dostępny w kilku dystrybucjach:

$ 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

Słabe wyjaśnienie: -e uuwyodrębni obiekt uu, -asprawi, że tablica będzie użyteczna (nie jestem pewien, czy poprawnie sformułowałem ten, ale w każdym razie…), -uzdekoduje ciąg, -pwróci do poprzedniego elementu (wydaje się, że -i NN jest dowolną liczbą, ma taki sam efekt) .

W zależności od przypadku wyniki mogą wymagać dodatkowego leczenia (jak twoje, jak widać).

Jshon nie wydaje się jednak odporny na zniekształcenie JSON (twoje „Tagi” przecinkami przed zamykającym nawiasem klamrowym spowodują błąd).

Ktoś wspomniał o jsawk w innym wątku, ale go nie testowałem.

Skippy le Grand Gourou
źródło
0

Oto sugestia jednej linijki:

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)

Nie idealnie, ale zadziałałoby, gdybyś go trochę ulepszył.

Zasadniczo używa prdo drukowania każdego zestawu wyników według kolumny. Każdy zestaw wyników jest zwracany przez podstawienie procesu, które analizuje plik JSON i zwraca wartości na podstawie klucza.

Działa to podobnie, jak opisano w: Biorąc pod uwagę treść klucz-wartość, jak grupować wartości według klucza i sortować według wartości?

kenorb
źródło
0

Spójrz na jtcnarzędzie cli:

pozwala na łatwe wydobycie wymaganych informacji z twojego jsona (zakładając, że jest w file.jsonśrodku, twój JSON musi zostać naprawiony, jest tam kilka dodatkowych przecinków):

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.
źródło
-2

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

tłumaczy plik Jason na coś, co można readeable:

{
  „wersja”: [
    „sessionrestore”,
    1
  ],
  „windows”: [
    {
      „tabs”: [
        {
          „wpisy”: [
            {
              „url”: „http://orf.at/#/stories/2.../”,
              „title”: „news.ORF.at”,
              „charset”: „UTF-8”,
              „ID”: 9588,
              „docshellID”: 298,
              „docIdentifier”: 10062,
              „trwały”: prawda
            },
...

Teraz powinno być możliwe parsowanie danych za pomocą standardowych narzędzi

Ternitz
źródło