¿Cómo analizar JSON con scripts de shell en Linux?

56

Tengo una salida JSON de la que necesito extraer algunos parámetros en Linux.

Esta es la salida 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
            }
        ]
    }

Quiero escribir un archivo que contenga encabezado como id de instancia, etiqueta como nombre, centro de costos, propietario. y debajo de ciertos valores de la salida JSON. La salida aquí dada es solo un ejemplo.

¿Cómo puedo hacer eso usando sedy awk?

Rendimiento esperado :

 Instance id         Name                           cost centre             Owner
    i-1234576          RDS_Machine (us-east-1c)        1234                   Jyoti
usuario3086014
fuente
1
Canalice su llamada CLI en python, sugerido porque es nativo de instancias EC2. Python puede interpretar fácilmente JSON. Vea la respuesta a continuación para ver un ejemplo. Por supuesto, también podría usar cualquier otro lenguaje SS, pero requerirán instalaciones mientras Python ya está allí.
Robbie Averill
¿ Qué hay de usar el nodo ?
Eliran Malka

Respuestas:

65

La disponibilidad de analizadores en casi todos los lenguajes de programación es una de las ventajas de JSON como formato de intercambio de datos.

En lugar de intentar implementar un analizador JSON, probablemente sea mejor usar una herramienta creada para el análisis JSON como jq o un lenguaje de script de propósito general que tenga una biblioteca JSON.

Por ejemplo, usando jq, puede extraer el ID de imagen del primer elemento de la matriz de instancias de la siguiente manera:

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

Alternativamente, para obtener la misma información usando la biblioteca JSON de Ruby:

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

No responderé a todas sus preguntas y comentarios revisados, pero espero que lo siguiente sea suficiente para comenzar.

Suponga que tiene un script Ruby que puede leer un STDIN y generar la segunda línea en su salida de ejemplo [0]. Ese guión podría parecerse a 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}"

¿Cómo podrías usar un guión para lograr tu objetivo? Bueno, supongamos que ya tienes lo siguiente:

  • un comando para enumerar todas sus instancias
  • un comando para obtener el json anterior para cualquier instancia en su lista y enviarlo a STDOU

Una forma sería usar su shell para combinar estas herramientas:

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

Ahora, tal vez tenga un solo comando que le dé un blob json para todas las instancias con más elementos en esa matriz de "Instancias". Bueno, si ese es el caso, solo tendrá que modificar un poco el script para recorrer la matriz en lugar de simplemente usar el primer elemento.

Al final, la forma de resolver este problema es la forma de resolver muchos problemas en Unix. Divídalo en problemas más fáciles. Encuentra o escribe herramientas para resolver el problema más fácil. Combine esas herramientas con su shell u otras características del sistema operativo.

[0] Tenga en cuenta que no tengo idea de dónde obtiene el centro de costos, así que lo inventé.

Steven D
fuente
He instalado jq en mi máquina. Pero no sé cómo obtener la información. Estoy actualizando la pregunta
user3086014
Como hacer eso. El comando ec2-describe instancia da reslut como este. estos son datos para 1 instancia, hay 100 instancias. Cómo hacer eso en una secuencia de comandos
user3086014
Tengo herramientas aws cli que me dan la salida. ahora cómo analizar la salida y las etiquetas requeridas que realmente no sé
user3086014
2
@ user3086014 Lo siento, pero no pondré más trabajo en esta respuesta. Echa un vistazo al ejemplo de Ruby que tengo allí. Debería darle un buen lugar para comenzar a obtener etiquetas de varias partes del JSON que desea.
Steven D
En la moltitude de las herramientas json disponibles, jq es mi stedolan.github.io/jq/manual favorito . Disponible en distribución estándar también. Un área de juegos para probar filtros está disponible en jqplay.org/jq?q=.&j=%22Hello%2C%20world!%22
lrkwz
15

Puede usar el siguiente script de Python para analizar esos datos. Supongamos que tiene datos JSON de matrices en archivos como array1.json, array2.jsonetc.

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

Y luego solo corre:

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

No he visto costos en sus datos, por eso no los incluí.

De acuerdo con la discusión en los comentarios, he actualizado el 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"] 

Puede intentar ejecutar el siguiente comando:

#ec2-describe-instance <instance> | python parse.py
Robert Jonczy
fuente
pero esta es solo una matriz, hay matrices similares devueltas por el comando. cómo hacer eso
usuario3086014
y estos datos son generados por el comando de instancia ec2-describe en tiempo de ejecución. cómo manejar eso
user3086014
He modificado un poco este script de 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() si tiene todos los datos json de las matrices en archivos como array1.json, array2.json, ... y así sucesivamente, podría intentar ejecutarlo de esta manera: # for x in ls * .json; do python parse.py $x; done
Robert Jonczy
Puedes actualizar la respuesta en sí. no es legible
user3086014
También tengo arrays.100 de arrays como este
user3086014
9

El siguiente código jq:

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

utilizado como:

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

daría salida:

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

Algunos consejos para entender el código:

  • from_entriestoma un conjunto de objetos como {key:a, value:b}y lo convierte en un objeto con los pares clave / valor correspondientes ( {a: b});
  • Las teclas Keyy Valueen la Tagsmatriz tenían que convertirse a minúsculas;
  • La última cadena utiliza la función de interpolación de cadenas de jq. Puede ajustarlo según sea necesario.

Para obtener más detalles, consulte el tutorial y el manual de jq en https://stedolan.github.io/jq/

Nadrieril
fuente
1
Ahora puede acortar la extracción de etiquetas usando (.Tags | map({Value, Key}) | from_entries) as $tags, sin convertir las claves a minúsculas.
mloughran
8

Otros han proporcionado respuestas generales para su pregunta que demuestran buenas formas de analizar json, sin embargo, como usted, estaba buscando una forma de extraer una identificación de instancia de aws usando una herramienta central como awk o sed sin depender de otros paquetes. Para lograr esto, puede pasar el argumento "--output = text" a su comando aws que le dará una cadena awk analizable. Con eso, simplemente puede obtener la ID de instancia usando algo como lo siguiente ...

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

Jshon está disponible en varias distribuciones:

$ 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

Mala explicación: -e uuextraerá el objeto uu, -ahará que la matriz sea utilizable (no estoy seguro de haber redactado correctamente este, pero de todos modos ...), -udecodificará la cadena, -pvolverá al elemento anterior (parece que -i N, siendo N cualquier número, tiene el mismo efecto) .

Dependiendo de su caso, la salida puede requerir algún tratamiento posterior (como el suyo, como puede ver).

Jshon sin embargo, no parece robusto frente a la malformación de JSON (sus "Etiquetas" con comas antes del cierre de la llave generarán un error).

Alguien mencionó jsawk en otro hilo, pero no lo he probado.

Skippy le Grand Gourou
fuente
0

Aquí hay una sugerencia:

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)

No es perfecto, pero funcionaría si lo modificas un poco.

Básicamente se utiliza prpara imprimir cada resultado del conjunto por columna. Cada conjunto de resultados se devuelve mediante la sustitución del proceso que analiza el archivo JSON y devuelve valores basados ​​en la clave.

Esto funciona de manera similar a como se describe en: Dado el contenido clave-valor, ¿cómo puedo agrupar los valores por clave y ordenar por valor?

kenorb
fuente
0

Echa un vistazo a la jtcherramienta cli:

permite extraer fácilmente la información requerida de su json (suponiendo que esté file.json, por cierto, su JSON necesita ser reparado, hay un par de comas adicionales allí):

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

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

traduce su archivo jason a algo legible como este:

{
  "versión": [
    "sessionrestore",
    1
  ],
  "ventanas": [
    {
      "pestañas": [
        {
          "entradas": [
            {
              "url": "http://orf.at/#/stories/2.../",
              "title": "news.ORF.at",
              "charset": "UTF-8",
              "ID": 9588,
              "docshellID": 298,
              "docIdentifier": 10062,
              "persistir": cierto
            },
...

Ahora debería ser posible analizar sus datos con cualquier herramienta estándar

Ternitz
fuente