如何在 Linux 中使用 shell 脚本解析 JSON?

use*_*014 78 sed awk text-processing json

我有一个 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
            }
        ]
    }
Run Code Online (Sandbox Code Playgroud)

我想编写一个文件,其中包含实例 id 等标题、名称等标签、成本中心、所有者。低于 JSON 输出中的某些值。这里给出的输出只是一个例子。

我怎样才能使用sedand做到这一点awk

预期输出:

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
Run Code Online (Sandbox Code Playgroud)

Ste*_*n D 80

几乎所有编程语言中的解析器的可用性是 JSON 作为数据交换格式的优势之一。

与其尝试实现 JSON 解析器,不如使用为 JSON 解析构建的工具(例如jq)或具有 JSON 库的通用脚本语言。

例如,使用 jq,您可以从 Instances 数组的第一项中提取 ImageID,如下所示:

jq '.Instances[0].ImageId' test.json
Run Code Online (Sandbox Code Playgroud)

或者,要使用 Ruby 的 JSON 库获取相同的信息:

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

我不会回答您修改后的所有问题和评论,但希望以下内容足以让您入门。

假设您有一个 Ruby 脚本,它可以从 STDIN 读取 a 并在示例 output[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}"
Run Code Online (Sandbox Code Playgroud)

您如何使用这样的脚本来完成您的整个目标?好吧,假设您已经有了以下内容:

  • 列出所有实例的命令
  • 获取列表中任何实例的上述 json 并将其输出到 STDOU 的命令

一种方法是使用您的 shell 来组合这些工具:

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

现在,也许您有一个命令可以为所有实例提供一个 json blob,其中“实例”数组中有更多项目。好吧,如果是这种情况,您只需要稍微修改脚本以遍历数组,而不是简单地使用第一项。

归根结底,解决这个问题的方法,就是在Unix中解决很多问题的方法。把它分解成更简单的问题。查找或编写工具来解决更简单的问题。将这些工具与您的 shell 或其他操作系统功能相结合。

[0] 请注意,我不知道您从哪里获得成本中心,所以我只是编造了它。

  • @ user3086014 对不起,但我不会在这个答案中投入更多的工作。看看我在那里的 Ruby 示例。它应该为您提供一个很好的起点,可以开始了解如何从您想要的 JSON 的各个部分中获取标签。 (3认同)

小智 16

您可以使用以下 python 脚本来解析该数据。让我们假设你在文件一样数组JSON数据array1.jsonarray2.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()
Run Code Online (Sandbox Code Playgroud)

然后运行:

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

我没有在你的数据中看到成本,这就是为什么我没有把它包括在内。

根据评论中的讨论,我更新了 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"] 
Run Code Online (Sandbox Code Playgroud)

您可以尝试运行以下命令:

#ec2-describe-instance <instance> | python parse.py
Run Code Online (Sandbox Code Playgroud)


小智 12

其他人已经为您的问题提供了一般性答案,这些答案展示了解析 json 的好方法,但是我和您一样,正在寻找一种方法来使用 awk 或 sed 等核心工具提取 aws 实例 ID,而不依赖于其他包。为此,您可以将“--output=text”参数传递给您的 aws 命令,该参数将为您提供一个 awk 可解析字符串。有了它,您可以简单地使用以下内容获取实例 ID...

aws ec2 run-instances --output text  | awk -F"\t" '$1=="INSTANCES" {print $8}'
Run Code Online (Sandbox Code Playgroud)


小智 9

以下jq代码:

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

像这样使用:

json_producer | jq -r '<jq code...>'
Run Code Online (Sandbox Code Playgroud)

会输出:

i-1234576 | RDS_Machine (us-east-1c) | 1234 | Jyoti Bhanot
Run Code Online (Sandbox Code Playgroud)

理解代码的几个要点:

  • from_entries获取一个对象数组 like{key:a, value:b}并将其转换为具有相应键/值对 ( {a: b}) 的对象;
  • 数组中的KeyValueTags必须转换为小写;
  • 最后一个字符串使用 jq 的字符串插值功能。您可以根据需要对其进行调整。

有关更多详细信息,请参阅https://stedolan.github.io/jq/ 上的jq 教程和手册