Como analisar JSON com scripts de shell no Linux?

56

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

Quero escrever um arquivo que contenha título como ID da instância, marca como nome, centro de custo, proprietário. e abaixo desse valor da saída JSON. A saída aqui dada é apenas um exemplo.

Como posso fazer isso usando sede awk?

Saída esperada:

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
user3086014
fonte
11
Canalize sua chamada da CLI para python, sugerida porque é nativa das instâncias do EC2. Python pode facilmente interpretar JSON. Veja a resposta abaixo para um exemplo. Obviamente, você também pode usar qualquer outra linguagem SS, mas elas exigirão instalações, enquanto o Python já está lá.
Robbie Averill
que tal usar o nó ?
Eliran Malka #

Respostas:

65

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

Em vez de tentar implementar um analisador JSON, provavelmente é melhor usar uma ferramenta criada para a análise JSON, como jq, ou uma linguagem de script de uso geral que possui uma biblioteca JSON.

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

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 responderei a todas as suas perguntas e comentários revisados, mas espero que o seguinte seja suficiente para você começar.

Suponha que você tenha um script Ruby que possa ler um de STDIN e gerar a segunda linha no exemplo de saída [0]. Esse script pode ser 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 realizar 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 da 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 fornece um blob json para todas as instâncias com mais itens na matriz "Instâncias". Bem, se for esse o caso, você precisará modificar um pouco o script para percorrer a matriz em vez de simplesmente usar o primeiro item.

No final, a maneira de resolver esse 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 seu shell ou outros recursos do sistema operacional.

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

Steven D
fonte
Eu instalei o jq na minha máquina. mas eu não sei como obter as informações. Estou atualizando a pergunta
user3086014
Como fazer isso. a instância do comando ec2-description fornece reslut assim. são dados para 1 instância, existem 100 instâncias. Como fazer isso em um script
user3086014 27/03
Eu tenho ferramentas aws cli que me dão a saída. agora como analisar a saída e as marcas necessárias que eu realmente não sei
user3086014
2
@ user3086014 Sinto muito, mas não vou colocar mais trabalho nessa resposta. Veja o exemplo de Ruby que tenho lá. Deve ser um bom ponto de partida para começar a obter tags de várias partes do JSON que você deseja.
Steven D
No moltitude das ferramentas json disponíveis, jq é meu stedolan.github.io/jq/manual favorito . Disponível também na distribuição padrão. Um playground para testes de filtros está disponível em jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz
15

Você pode usar o seguinte script python para analisar esses dados. Vamos supor que você tem dados JSON de matrizes em arquivos como array1.json, array2.jsone 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 então apenas execute:

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

Não vi custos nos seus dados, por isso não incluí isso.

De acordo com a discussão nos comentários, eu 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
Robert Jonczy
fonte
mas essa é apenas uma matriz; há matrizes semelhantes retornadas pelo comando. como fazer isso #
318601414
e esses dados são gerados pelo comando de instância ec2-description em tempo de execução. como lidar com isso
user3086014
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() Modifiquei um pouco esse script python: se você tiver todos os dados json de matrizes em arquivos como array1.json, array2.json, ... e assim por diante, tente executar da seguinte maneira: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy
você pode atualizar a resposta em si. Não é possível ler o
código
Também tenho arrays.100 de matrizes como este
user3086014
9

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_entriespega uma matriz de objetos como {key:a, value:b}e a transforma em um objeto com os pares chave / valor correspondentes ( {a: b});
  • As chaves Keye Valuena Tagsmatriz tiveram que ser convertidas para minúsculas;
  • A última string usa o recurso de interpolação de string do jq. Você pode ajustá-lo conforme necessário.

Para obter mais detalhes, consulte o tutorial e o manual do jq em https://stedolan.github.io/jq/

Nadrieril
fonte
11
Agora você pode reduzir a extração de tags usando (.Tags | map({Value, Key}) | from_entries) as $tags, sem converter chaves em minúsculas.
mloughran
8

Outros forneceram respostas gerais para sua pergunta que demonstram boas maneiras de analisar o json, no entanto, como você, estava procurando uma maneira de extrair um ID de instância do aws usando uma ferramenta principal como o awk ou o sed sem depender de outros pacotes. Para fazer isso, você pode passar o argumento "--output = text" para o seu comando aws, que fornecerá uma string aws parsable. 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}'
Mick Giles
fonte
3

O 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

Má explicação: -e uuextrairá o objeto uu, -atornará a matriz utilizável (não tenho certeza de que eu escrevi corretamente esta, mas enfim…), -udecodificará a string, -pvoltará 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 No entanto, parece robusto contra a malformação JSON (suas "Tags" com vírgulas antes do colchete de fechamento causam um erro).

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

Skippy le Grand Gourou
fonte
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 funcionaria se você o ajustar um pouco.

É basicamente usando prpara imprimir cada resultado definido por coluna. Cada conjunto de resultados é retornado por substituição de processo que analisa o arquivo JSON e retorna valores com base na chave.

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

kenorb
fonte
0

Dê uma olhada na jtcferramenta cli:

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

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

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

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

{
  "version": [
    "sessionrestore",
    1 1
  ],
  "janelas": [
    {
      "guias": [
        {
          "entradas": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "charset": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "persistir": verdadeiro
            }
...

Agora deve ser possível analisar seus dados com qualquer ferramenta padrão

Ternitz
fonte