Bagaimana cara mem-parse JSON dengan shell scripting di Linux?

56

Saya memiliki output JSON dari mana saya perlu mengekstrak beberapa parameter di Linux.

Ini adalah 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
            }
        ]
    }

Saya ingin menulis file yang berisi tajuk seperti id instan, tag seperti nama, pusat biaya, pemilik. dan di bawah itu nilai-nilai tertentu dari output JSON. Output yang diberikan di sini hanyalah sebuah contoh.

Bagaimana saya bisa melakukan itu menggunakan seddan awk?

Output yang diharapkan:

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
pengguna3086014
sumber
1
Pipa panggilan CLI Anda menjadi python, disarankan karena ini asli untuk instance EC2. Python dapat dengan mudah menafsirkan JSON. Lihat jawaban di bawah untuk contoh. Tentu saja, Anda bisa menggunakan bahasa SS lainnya juga, tetapi mereka akan memerlukan instalasi sedangkan Python sudah ada di sana.
Robbie Averill
bagaimana dengan menggunakan simpul ?
Eliran Malka

Jawaban:

65

Ketersediaan parser di hampir setiap bahasa pemrograman adalah salah satu keunggulan JSON sebagai format pertukaran-data.

Daripada mencoba mengimplementasikan parser JSON, Anda mungkin lebih baik menggunakan alat yang dibuat untuk parsing JSON seperti jq atau bahasa skrip tujuan umum yang memiliki pustaka JSON.

Misalnya, menggunakan jq, Anda bisa mengeluarkan ImageID dari item pertama array Instances sebagai berikut:

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

Atau, untuk mendapatkan informasi yang sama menggunakan perpustakaan JSON Ruby:

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

Saya tidak akan menjawab semua pertanyaan dan komentar Anda yang telah direvisi, tetapi yang berikut cukup mudah untuk membantu Anda memulai.

Misalkan Anda memiliki skrip Ruby yang dapat membaca dari STDIN dan menampilkan baris kedua dalam contoh keluaran Anda [0]. Skrip itu mungkin terlihat seperti:

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

Bagaimana Anda bisa menggunakan naskah seperti itu untuk mencapai seluruh tujuan Anda? Nah, misalkan Anda sudah memiliki yang berikut ini:

  • perintah untuk membuat daftar semua instance Anda
  • perintah untuk mendapatkan json di atas untuk setiap instance pada daftar Anda dan output ke STDOU

Salah satu caranya adalah dengan menggunakan shell Anda untuk menggabungkan alat-alat ini:

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

Sekarang, mungkin Anda memiliki perintah tunggal yang memberi Anda satu json blob untuk semua instance dengan item lebih banyak dalam array "Instances" itu. Nah, jika itu masalahnya, Anda hanya perlu sedikit memodifikasi skrip untuk beralih melalui array daripada hanya menggunakan item pertama.

Pada akhirnya, cara untuk menyelesaikan masalah ini, adalah cara untuk menyelesaikan banyak masalah di Unix. Hancurkan menjadi masalah yang lebih mudah. Temukan atau tulis alat untuk menyelesaikan masalah yang lebih mudah. Gabungkan alat-alat itu dengan shell Anda atau fitur sistem operasi lain.

[0] Perhatikan bahwa saya tidak tahu dari mana Anda mendapatkan pusat biaya, jadi saya hanya mengada-ada.

Steven D
sumber
saya telah menginstal jq di mesin saya. tetapi saya tidak tahu bagaimana cara mendapatkan informasi. saya memperbarui pertanyaan
user3086014
Bagaimana cara melakukannya. perintah ec2-menggambarkan instance memberikan reslut seperti ini. ini adalah data untuk 1 instance, ada 100 instance. Cara melakukannya dalam skrip
user3086014
saya punya alat aws cli yang memberi saya output. sekarang bagaimana mem-parsing output dan tag yang diperlukan yang saya benar-benar tidak tahu
user3086014
2
@ user3086014 Maaf, tapi saya tidak akan melakukan lebih banyak pekerjaan dalam jawaban ini. Lihatlah contoh Ruby yang saya miliki di sana. Seharusnya memberi Anda tempat yang baik untuk memulai tentang cara mendapatkan tag dari berbagai bagian JSON yang Anda inginkan.
Steven D
Dalam moltitude alat json tersedia jq adalah stedolan.github.io/jq/manual favorit saya . Tersedia dalam distribusi std juga. Taman bermain untuk filter pengujian tersedia di jqplay.org/jq?q=.&j=%22Halo%2C%20world!%22
lrkwz
15

Anda bisa menggunakan skrip python berikut untuk mengurai data itu. Mari kita asumsikan bahwa Anda memiliki data JSON dari array dalam file seperti array1.json, array2.jsondan seterusnya.

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

Dan kemudian jalankan:

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

Saya belum melihat biaya dalam data Anda, itu sebabnya saya tidak memasukkannya.

Menurut diskusi dalam komentar, saya telah memperbarui skrip 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"] 

Anda dapat mencoba menjalankan perintah berikut:

#ec2-describe-instance <instance> | python parse.py
Robert Jonczy
sumber
tapi ini hanya satu array ada array yang sama yang dikembalikan oleh perintah. bagaimana melakukan itu
user3086014
dan data ini dihasilkan oleh perintah instance ec2-menggambarkan saat runtime. bagaimana mengatasinya
user3086014
Saya telah memodifikasi sedikit skrip python ini: 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() Jika Anda memiliki semua data json dari array dalam file seperti array1.json, array2.json, ... dan seterusnya, Anda dapat mencoba menjalankannya seperti ini: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy
Anda dapat memperbarui jawabannya sendiri. itu tidak terbaca
user3086014
Saya juga punya array array array seperti ini
user3086014
9

Kode jq berikut:

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

digunakan seperti:

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

akan menghasilkan:

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

Beberapa petunjuk untuk memahami kode:

  • from_entriesmengambil larik objek seperti {key:a, value:b}dan mengubahnya menjadi objek dengan pasangan kunci / nilai yang sesuai ( {a: b});
  • The Keydan Valuekunci dalam Tagsarray yang harus dikonversi ke huruf kecil;
  • String terakhir menggunakan fitur interpolasi string jq. Anda dapat mengubah sesuai kebutuhan.

Untuk detail lebih lanjut, lihat tutorial dan manual jq di https://stedolan.github.io/jq/

Nadrieril
sumber
1
Anda sekarang dapat mempersingkat ekstraksi tag menggunakan (.Tags | map({Value, Key}) | from_entries) as $tags, tanpa mengubah kunci menjadi huruf kecil.
mloughran
8

Orang lain telah memberikan jawaban umum untuk pertanyaan Anda yang menunjukkan cara-cara yang baik untuk mem-parsing json namun saya, seperti Anda, sedang mencari cara untuk mengekstrak id contoh aws menggunakan alat inti seperti awk atau sed tanpa tergantung pada paket lain. Untuk mencapai ini, Anda bisa meneruskan argumen "--output = text" ke perintah aws Anda yang akan memberi Anda string yang dapat diuraikan awk. Dengan itu Anda bisa mendapatkan ID instan menggunakan sesuatu seperti yang berikut ...

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

Jshon tersedia dalam beberapa distribusi:

$ 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

Penjelasan yang buruk: -e uuakan mengekstrak objek uu, -aakan membuat array dapat digunakan (tidak yakin saya dengan benar mengutarakan yang satu ini tetapi toh ...), -uakan mendekodekan string, -pakan kembali ke item sebelumnya (sepertinya -i N, N menjadi nomor berapa pun, memiliki efek yang sama) .

Tergantung pada kasus Anda, hasilnya mungkin memerlukan beberapa pasca perawatan (seperti milik Anda, seperti yang Anda lihat).

Jshon sepertinya tidak kuat terhadap malformasi JSON, ("Tag" Anda dengan koma sebelum braket keriting penutup akan menimbulkan kesalahan).

Seseorang menyebutkan jsawk di utas lain, tapi saya belum mengujinya.

Skippy le Grand Gourou
sumber
0

Berikut ini saran satu baris:

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)

Tidak sempurna, tetapi itu akan berhasil jika Anda mengubah sedikit.

Ini pada dasarnya menggunakan pruntuk mencetak setiap hasil yang ditetapkan per kolom. Setiap set hasil dikembalikan oleh proses substitusi yang mem-parsing file JSON dan mengembalikan nilai berdasarkan kunci.

Ini bekerja sama seperti yang dijelaskan dalam: Konten nilai kunci yang diberikan, bagaimana cara saya mengelompokkan nilai berdasarkan kunci dan mengurutkan berdasarkan nilai?

kenorb
sumber
0

Lihatlah jtccli tool:

itu memungkinkan dengan mudah mengekstraksi informasi yang diperlukan dari json Anda (dengan asumsi itu ada file.json, btw, JSON Anda perlu diperbaiki, ada beberapa koma tambahan di sana):

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

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

menerjemahkan file jason Anda ke sesuatu yang dapat digunakan kembali seperti ini:

{
  "versi": [
    "sessionrestore",
    1
  ],
  "windows": [
    {
      "tabs": [
        {
          "entri": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "charset": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "bertahan": benar
            },
...

Sekarang mungkin untuk mem-parsing data Anda dengan alat standar apa pun

Ternitz
sumber