วิธีการแยก JSON ด้วยเชลล์สคริปต์ใน Linux?


56

ฉันมีเอาต์พุต JSON ซึ่งฉันต้องการแยกพารามิเตอร์บางอย่างใน Linux

นี่คือเอาต์พุต 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
            }
        ]
    }

ฉันต้องการเขียนไฟล์ที่มีหัวเรื่องเช่น id เช่น, แท็กเช่นชื่อ, ศูนย์ต้นทุน, เจ้าของ และต่ำกว่าค่าที่แน่นอนจากเอาต์พุต JSON ผลลัพธ์ที่ได้รับที่นี่เป็นเพียงตัวอย่าง

ฉันจะใช้sedและทำสิ่งนั้นได้awkอย่างไร

ผลลัพธ์ที่คาดหวัง:

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

1
ไพพ์ไปที่การเรียก CLI ของคุณเป็นไพ ธ อนแนะนำเพราะมันเป็นอินสแตนซ์ของ EC2 Python สามารถตีความ JSON ได้อย่างง่ายดาย ดูคำตอบด้านล่างสำหรับตัวอย่าง แน่นอนคุณสามารถใช้ภาษา SS อื่น ๆ ด้วย แต่พวกเขาจะต้องติดตั้งในขณะที่ Python มีอยู่แล้ว
Robbie Averill

วิธีการเกี่ยวกับการใช้โหนด ?
Eliran Malka

คำตอบ:


65

ความพร้อมใช้งานของตัวแยกวิเคราะห์ในเกือบทุกภาษาการเขียนโปรแกรมเป็นหนึ่งในข้อดีของ JSON ในรูปแบบการแลกเปลี่ยนข้อมูล

แทนที่จะพยายามใช้ตัวแยกวิเคราะห์ JSON คุณน่าจะดีกว่าการใช้เครื่องมือที่สร้างขึ้นสำหรับการแยกวิเคราะห์ JSON เช่นjqหรือภาษาสคริปต์วัตถุประสงค์ทั่วไปที่มีไลบรารี JSON

ตัวอย่างเช่นการใช้ jq คุณสามารถดึง ImageID จากรายการแรกของอาร์เรย์อินสแตนซ์ดังนี้:

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

อีกวิธีหนึ่งเพื่อรับข้อมูลเดียวกันโดยใช้ไลบรารี JSON ของรูบี้:

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

ฉันจะไม่ตอบคำถามและความคิดเห็นที่ได้รับการแก้ไขทั้งหมดของคุณ แต่หวังว่าต่อไปนี้จะทำให้คุณเริ่มต้นได้

สมมติว่าคุณมีสคริปต์ Ruby ที่สามารถอ่านจาก STDIN และส่งออกบรรทัดที่สองในตัวอย่างผลลัพธ์ของคุณ [0] สคริปต์นั้นอาจมีลักษณะดังนี้:

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

คุณจะใช้สคริปต์เช่นนี้เพื่อบรรลุเป้าหมายทั้งหมดของคุณได้อย่างไร เอาล่ะสมมติว่าคุณมีสิ่งต่อไปนี้:

  • คำสั่งเพื่อแสดงรายการอินสแตนซ์ทั้งหมดของคุณ
  • คำสั่งเพื่อรับ json ด้านบนสำหรับอินสแตนซ์ใด ๆ ในรายการของคุณและส่งออกไปยัง STDOU

วิธีหนึ่งคือใช้เปลือกของคุณเพื่อรวมเครื่องมือเหล่านี้:

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

ตอนนี้บางทีคุณอาจมีคำสั่งเดียวที่ให้ json blob หนึ่งรายการสำหรับอินสแตนซ์ทั้งหมดที่มีรายการเพิ่มเติมในอาร์เรย์ "อินสแตนซ์" ถ้าเป็นเช่นนั้นคุณจะต้องแก้ไขสคริปต์เล็กน้อยเพื่อวนซ้ำในอาร์เรย์แทนที่จะใช้เพียงรายการแรก

ในที่สุดวิธีการแก้ปัญหานี้เป็นวิธีการแก้ปัญหามากมายใน Unix ทำลายมันให้เป็นปัญหาที่ง่ายขึ้น ค้นหาหรือเขียนเครื่องมือเพื่อแก้ปัญหาที่ง่ายขึ้น รวมเครื่องมือเหล่านั้นเข้ากับเชลล์หรือคุณสมบัติระบบปฏิบัติการอื่น ๆ

[0] โปรดทราบว่าฉันไม่มีความคิดว่าคุณจะได้ศูนย์ต้นทุนมาจากไหนดังนั้นฉันจึงตัดสินใจ


ฉันได้ติดตั้ง jq บนเครื่องของฉันแล้ว แต่ฉันไม่รู้วิธีรับข้อมูล ฉันกำลังอัปเดตคำถาม
3086014

วิธีการทำ คำสั่งอินสแตนซ์อธิบาย EC2 ให้ reslut เช่นนี้ นี่คือข้อมูลสำหรับ 1 อินสแตนซ์มี 100 อินสแตนซ์ วิธีการทำเช่นนั้นในสคริปต์
user3086014

ฉันมีเครื่องมือ aws cli ที่ให้ผลลัพธ์ฉัน ตอนนี้วิธีแยกวิเคราะห์ผลลัพธ์และแท็กที่จำเป็นซึ่งฉันไม่รู้จริงๆ
user3086014

2
@ user3086014 ฉันขอโทษ แต่ฉันจะไม่ทำงานเพิ่มเติมในคำตอบนี้ ลองดูตัวอย่าง Ruby ที่ฉันมีอยู่ มันควรจะเป็นจุดเริ่มต้นที่ดีในการดึงแท็กออกจากส่วนต่างๆของ JSON ที่คุณต้องการ
Steven D

ใน moltitude ของเครื่องมือ JSON ที่มี JQ เป็นที่ชื่นชอบstedolan.github.io/jq/manual มีจำหน่ายในรูปแบบ std เช่นกัน สนามเด็กเล่นสำหรับตัวกรองการทดสอบมีให้ที่jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz

15

คุณสามารถใช้สคริปต์ไพ ธ อนต่อไปนี้เพื่อวิเคราะห์ข้อมูล ช่วยให้สมมติว่าคุณมี JSON ข้อมูลจากอาร์เรย์ในไฟล์เช่นarray1.json, array2.jsonและอื่น ๆ

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

จากนั้นเพียงแค่เรียกใช้:

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

ฉันไม่ได้เห็นค่าใช้จ่ายในข้อมูลของคุณนั่นคือสาเหตุที่ฉันไม่ได้รวมไว้

จากการสนทนาในความคิดเห็นฉันได้อัปเดตสคริปต์ 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"] 

คุณสามารถลองเรียกใช้คำสั่งต่อไปนี้:

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

แต่นี่เป็นเพียงหนึ่งอาร์เรย์ที่มีอาร์เรย์ที่คล้ายกันซึ่งถูกส่งคืนโดยคำสั่ง วิธีการทำเช่นนั้น
user3086014

และข้อมูลนี้สร้างขึ้นโดยคำสั่งอินสแตนซ์อธิบาย EC2 ที่รันไทม์ วิธีจัดการ
user3086014

ฉันได้แก้ไขสคริปต์ 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() หากคุณมีข้อมูล json ทั้งหมดจากอาร์เรย์ในไฟล์เช่น array1.json, array2.json, ... และอื่น ๆ คุณสามารถลองเรียกใช้ดังนี้: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy

คุณสามารถอัปเดตคำตอบได้ มันไม่สามารถอ่านได้
user3086014

ฉันยังมีอาร์เรย์ 1,400 ของอาร์เรย์เช่นนี้
user3086014

9

รหัส jq ต่อไปนี้:

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

ใช้เหมือน:

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

จะส่งออก:

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

ตัวชี้สองสามตัวที่จะเข้าใจโค้ด:

  • from_entriesนำอาร์เรย์ของวัตถุที่ชอบ{key:a, value:b}และเปลี่ยนมันเป็นวัตถุที่มีคู่ของคีย์ / ค่าที่สอดคล้องกัน ( {a: b});
  • KeyและValueกุญแจในTagsอาร์เรย์จะต้องมีการแปลงเป็นตัวพิมพ์เล็ก;
  • สตริงสุดท้ายใช้คุณลักษณะการแก้ไขสตริงของ jq คุณสามารถปรับแต่งได้ตามต้องการ

ดูรายละเอียดเพิ่มเติมไปที่บทแนะนำและคู่มือการใช้งาน jq ที่https://stedolan.github.io/jq/


1
ตอนนี้คุณสามารถย่อการแยกแท็กโดยใช้(.Tags | map({Value, Key}) | from_entries) as $tagsโดยไม่ต้องแปลงคีย์เป็นตัวพิมพ์เล็ก
mloughran

8

คนอื่น ๆ ได้ให้คำตอบทั่วไปสำหรับคำถามของคุณซึ่งแสดงวิธีการแยกวิเคราะห์ json ที่ดี แต่ฉันเหมือนคุณกำลังมองหาวิธีที่จะดึง id อินสแตนซ์ aws โดยใช้เครื่องมือหลักเช่น awk หรือ sed โดยไม่ขึ้นอยู่กับแพ็คเกจอื่น ๆ ในการทำสิ่งนี้ให้สำเร็จคุณสามารถส่งผ่านอาร์กิวเมนต์ "--output = text" ไปยังคำสั่ง aws ของคุณซึ่งจะให้สตริง awk ที่แยกวิเคราะห์ได้ เมื่อคุณได้รับ ID อินสแตนซ์แล้วใช้สิ่งต่อไปนี้ ...

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

3

Jshonมีให้บริการในหลายดิสทริบิวชัน:

$ 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

คำอธิบายที่ไม่ดี: -e uuจะดึงวัตถุuu, -aจะทำให้อาร์เรย์ที่ใช้งานได้ (ไม่แน่ใจว่าผมได้อย่างถูกต้อง phrased นี้ แต่อย่างไรก็ตาม ... ), -uจะถอดรหัสสตริง-pจะกลับไปยังรายการก่อนหน้า (ดูเหมือนว่า-i N, N เป็นจำนวนใด ๆ ที่มีผลกระทบเหมือนกัน) .

ขึ้นอยู่กับกรณีของคุณผลลัพธ์อาจต้องการการรักษาหลังการผ่าตัด (เช่นเดียวกับคุณตามที่คุณเห็น)

Jshon ดูเหมือนจะไม่แข็งแกร่งต่อความผิดปกติของ JSON แต่ ("แท็ก" ของคุณด้วยเครื่องหมายจุลภาคก่อนวงเล็บเหลี่ยมปิดจะทำให้เกิดข้อผิดพลาด)

มีคนพูดถึงjsawkในเธรดอื่น แต่ฉันยังไม่ได้ทดสอบ



0

นี่คือข้อเสนอแนะหนึ่งซับ:

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)

ไม่สมบูรณ์แบบ แต่ใช้งานได้หากคุณปรับแต่งเล็กน้อย

โดยทั่วไปprจะใช้เพื่อพิมพ์ผลลัพธ์แต่ละชุดต่อคอลัมน์ แต่ละชุดผลลัพธ์ถูกส่งคืนโดยการทดแทนกระบวนการซึ่งแจงไฟล์ JSON และส่งคืนค่าตามคีย์

สิ่งนี้ทำงานคล้ายกับที่อธิบายไว้ใน: รับเนื้อหาของคีย์ - ค่าฉันจะจัดกลุ่มค่าตามคีย์และเรียงตามค่าได้อย่างไร


0

ลองดูjtcเครื่องมือ cli:

มันช่วยให้สามารถดึงข้อมูลที่ต้องการจาก json ของคุณได้อย่างง่ายดาย (สมมุติว่ามันอยู่ใน file.jsonbtw, JSON ของคุณต้องได้รับการแก้ไข

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

แปลไฟล์ jason ของคุณเป็นสิ่งที่ readeable เช่นนี้:

{
  "รุ่น": [
    "sessionrestore"
    1
  ]
  "windows": [
    {
      "แท็บ": [
        {
          "รายการ": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "ชุดอักขระ": "UTF-8",
              "ID": 9588
              "docshellID": 298,
              "docIdentifier": 10062,
              "คงอยู่": จริง
            }
...

ตอนนี้มันเป็นไปได้ที่จะวิเคราะห์ข้อมูลของคุณด้วยเครื่องมือมาตรฐาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.