Como analisar o JSON com shell script no Linux?

46

Eu tenho uma saída JSON da qual preciso extrair alguns parâmetros no Linux.

Esta é a saída 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
            }
        ]
    }

Eu quero escrever um arquivo que contenha título como id de instância, tag como nome, centro de custo, proprietário. e abaixo disso certos valores da saída JSON. A saída aqui dada é apenas um exemplo.

Como posso fazer isso usando sed e awk ?

Resultado esperado:

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
    
por user3086014 27.03.2014 / 06:01

9 respostas

56

A disponibilidade de analisadores em quase todas as linguagens de programação é uma das vantagens do JSON como formato de troca de dados.

Em vez de tentar implementar um analisador JSON, é melhor usar uma ferramenta criada para análise JSON, como jq ou uma linguagem de script de propósito geral que tenha uma biblioteca JSON.

Por exemplo, usando jq, você pode extrair o ImageID do primeiro item da matriz Instances da seguinte forma:

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

Como alternativa, para obter as mesmas informações usando a biblioteca JSON do Ruby:

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

Não vou responder a todas as suas perguntas e comentários revisados, mas o que se segue é o suficiente para você começar.

Suponha que você tenha um script Ruby que possa ler um de STDIN e gerar a segunda linha em sua saída de exemplo [0]. Esse script pode parecer algo como:

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

Como você pode usar esse script para atingir todo o seu objetivo? Bem, suponha que você já tenha o seguinte:

  • um comando para listar todas as suas instâncias
  • um comando para obter o json acima para qualquer instância em sua lista e enviá-lo para STDOU

Uma maneira seria usar seu shell para combinar essas ferramentas:

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

Agora, talvez você tenha um único comando que forneça um json blob para todas as instâncias com mais itens nesse array "Instances". Bem, se for esse o caso, você só precisará modificar o script um pouco para percorrer a matriz em vez de simplesmente usar o primeiro item.

No final, a maneira de resolver este problema, é a maneira de resolver muitos problemas no Unix. Divida-o em problemas mais fáceis. Encontre ou escreva ferramentas para resolver o problema mais fácil. Combine essas ferramentas com o seu shell ou com outros recursos do sistema operacional.

[0] Note que eu não tenho ideia de onde você obtém o centro de custo, então eu apenas inventei.

    
por 27.03.2014 / 10:21
14

Você pode usar o seguinte script python para analisar esses dados. Vamos supor que você tenha dados JSON de matrizes em arquivos como array1.json , array2.json e assim por diante.

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

E depois é só executar:

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

Eu não vi custo nos seus dados, por isso não incluí isso.

De acordo com a discussão nos comentários, atualizei o script 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"] 

Você pode tentar executar o seguinte comando:

#ec2-describe-instance <instance> | python parse.py
    
por 27.03.2014 / 11:30
8

O seguinte código jq:

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

usado como:

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

produziria:

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

Alguns ponteiros para entender o código:

  • from_entries usa uma matriz de objetos como {key:a, value:b} e a transforma em um objeto com pares de chave / valor correspondentes ( {a: b} );
  • As chaves Key e Value na matriz Tags tiveram que ser convertidas em minúsculas;
  • A última string usa o recurso de interpolação de strings do jq. Você pode ajustá-lo conforme necessário.

Para mais detalhes, veja o tutorial e manual do jq no link

    
por 06.04.2015 / 21:40
7

Outros forneceram respostas gerais para sua pergunta, que demonstram boas maneiras de analisar json. No entanto, como você, eu estava procurando uma maneira de extrair um id de instância aws usando uma ferramenta principal como awk ou sed sem depender de outros pacotes. Para conseguir isso, você pode passar o argumento "--output = text" para o seu comando aws, que lhe dará uma string awk analisável. Com isso, você pode simplesmente obter o ID da instância usando algo como o seguinte ...

aws ec2 run-instances --output text  | awk -F"\t" '$1=="INSTANCES" {print $8}'
    
por 10.12.2014 / 22:26
3

Jshon está disponível em várias distribuições:

$ 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

Uma explicação ruim: -e uu extrairá o objeto uu , -a tornará o array utilizável (não tenho certeza se escrevi corretamente, mas mesmo assim ...), -u decodificará a string, -p irá de volta ao item anterior (parece que -i N , N sendo qualquer número, tem o mesmo efeito).

Dependendo do seu caso, a saída pode exigir algum pós-tratamento (como o seu, como você pode ver).

Jshon parece não ser robusto com a má formação de JSON, embora (suas "Tags" com vírgulas antes do fechamento de chaves gerará um erro).

Alguém mencionou jsawk em outro tópico, mas eu não testei isso.

    
por 05.09.2015 / 23:44
1

Se isso estiver limitado ao caso de uso da AWS fornecido acima, você deverá usar os sinalizadores --query e --output para sua chamada da CLI API

link

    
por 29.11.2017 / 15:18
0

Aqui está uma sugestão de uma linha:

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)

Não é perfeito, mas funciona se você ajustar um pouco.

Basicamente, usando pr para imprimir cada resultado definido por coluna. Cada conjunto de resultados é retornado pela substituição do processo, que analisa o arquivo JSON e retorna os valores com base na chave.

Isso funciona de maneira semelhante à descrita em: Dado o conteúdo de valor-chave, como eu agrupo valores por chave e classifico por valor?

    
por 27.04.2015 / 17:58
0

Dê uma olhada na jtc ferramenta cli:

ele permite extrair facilmente as informações necessárias do seu json (supondo que esteja em file.json , btw, seu JSON precisa ser corrigido, há algumas vírgulas extras lá):

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 $ 
    
por 09.11.2018 / 22:49
-2

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

traduz seu arquivo jason para algo legível como este:

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

Agora, deve ser possível analisar seus dados com quaisquer ferramentas padrão

    
por 28.11.2016 / 13:51