wake-up-neo.net

Wie formatiere ich einen JSON-String als Tabelle mit jq?

Ich habe gerade mit Bash-Scripting angefangen und bin über jq gestolpert, um mit JSON zu arbeiten.

Ich muss einen JSON-String wie folgt in eine Tabelle für die Ausgabe im Terminal umwandeln.

[{
    "name": "George",
    "id": 12,
    "email": "[email protected]"
}, {
    "name": "Jack",
    "id": 18,
    "email": "[email protected]"
}, {
    "name": "Joe",
    "id": 19,
    "email": "[email protected]"
}]

Was ich im Terminal anzeigen möchte:

ID        Name
=================
12        George
18        Jack
19        Joe

Beachten Sie, dass ich die E-Mail-Eigenschaft nicht für jede Zeile anzeigen möchte. Daher sollte der Befehl jq eine Filterung enthalten. Das Folgende gibt mir eine einfache Liste von Namen und IDs:

list=$(echo "$data" | jq -r '.[] | .name, .id')
printf "$list"

Das Problem dabei ist, dass ich es nicht wie eine Tabelle anzeigen kann. Ich weiß, dass jq einige Formatierungsoptionen hat, aber nicht annähernd so gut wie die Optionen, die ich bei der Verwendung von printf habe. Ich denke, ich möchte diese Werte in einem Array erhalten, das ich dann durchlaufen kann, um die Formatierung durchzuführen ...? Die Dinge, die ich ausprobiert habe, haben unterschiedliche Ergebnisse, aber niemals das, was ich wirklich will.

Kann mir jemand die richtige Richtung zeigen?

31
Rein

Warum nicht so etwas wie:

echo '[{
    "name": "George",
    "id": 12,
    "email": "[email protected]"
}, {
    "name": "Jack",
    "id": 18,
    "email": "[email protected]"
}, {
    "name": "Joe",
    "id": 19,
    "email": "[email protected]"
}]' | jq -r '.[] | "\(.id)\t\(.name)"'

Ausgabe

12  George
18  Jack
19  Joe

Edit 1: Für feinkörnige Formatierungen verwenden Sie Tools wie awk

 echo '[{
    "name": "George",
    "id": 12,
    "email": "[email protected]"
}, {
    "name": "Jack",
    "id": 18,
    "email": "[email protected]"
}, {
    "name": "Joe",
    "id": 19,
    "email": "[email protected]"
}]' | jq -r '.[] | [.id, .name] | @csv' | awk -v FS="," 'BEGIN{print "ID\tName";print "============"}{printf "%s\t%s%s",$1,$2,ORS}'
ID  Name
============
12  "George"
18  "Jack"
19  "Joe"

Edit 2: Antwort auf

Es gibt keine Möglichkeit, eine Variable direkt mit einem Array zu erhalten von jq?

Warum nicht?

Ein etwas ausführliches Beispiel (in der Tat von Ihnen modifiziert), in dem E-Mails in ein Array umgewandelt werden, zeigt dies

echo '[{
    "name": "George",
    "id": 20,
    "email": [ "[email protected]" , "[email protected]" ]
}, {
    "name": "Jack",
    "id": 18,
    "email": [ "[email protected]" , "[email protected]" ]
}, {
    "name": "Joe",
    "id": 19,
    "email": [ "[email protected]" ]
}]' | jq -r '.[] | .email'

Ausgabe

[
  "[email protected]",
  "[email protected]"
]
[
  "[email protected]",
  "[email protected]"
]
[
  "[email protected]"
]
31
sjsam

Die Verwendung des @tsv-Filters ist sehr zu empfehlen, vor allem, weil er zahlreiche "Edge-Fälle" standardmäßig behandelt:

.[] | [.id, .name] | @tsv

Das Hinzufügen der Header kann wie folgt erfolgen:

jq -r '["ID","NAME"], ["--","------"], (.[] | [.id, .name]) | @tsv'

Das Ergebnis:

ID  NAME
--  ------
12  George
18  Jack
19  Joe
40
peak

Das Definieren von Headern von Hand ist suboptimal! Das Auslassen von Headern ist ebenfalls nicht optimal.

TL; DR

$ grep -e . /tmp/data /tmp/jq-scrigt 
/tmp/data:[{ "name": "George", "id": 12, "email": "[email protected]" },
/tmp/data:{ "name": "Jack", "id": 18, "email": "[email protected]" }, 
/tmp/data:{ "name": "Joe", "id": 19, "email": "[email protected]" }]
/tmp/jq-scrigt:  [.[]| with_entries( .key |= ascii_downcase ) ]
/tmp/jq-scrigt:|    (.[0] |keys_unsorted | @tsv)
/tmp/jq-scrigt:   , (.[]|.|map(.) |@tsv)


$ < /tmp/data jq -rf /tmp/jq-scrigt  | column -t
name    id  email
George  12  [email protected]
Jack    18  [email protected]
Joe     19  [email protected]

Ich habe diese Frage gefunden, als ich einige Daten von Amazon Web Services zusammenfasste. Das Problem, an dem ich gearbeitet habe, falls Sie ein anderes Beispiel wünschen:

$ aws ec2 describe-spot-instance-requests | tee /tmp/ins |
    jq  --raw-output '
                                     # extract instances as a flat list.
    [.SpotInstanceRequests | .[] 
                                     # remove unwanted data
    | { 
        State, 
        statusCode: .Status.Code, 
        type: .LaunchSpecification.InstanceType, 
        blockPrice: .ActualBlockHourlyPrice, 
        created: .CreateTime, 
        SpotInstanceRequestId}
    ] 
                                        # lowercase keys
                                        # (for predictable sorting, optional)
    |  [.[]| with_entries( .key |= ascii_downcase ) ]
        |    (.[0] |keys_unsorted | @tsv)               # print headers
           , (.[]|.|map(.) |@tsv)                       # print table
    ' | column -t

Ausgabe:

state      statuscode                   type     blockprice  created                   spotinstancerequestid
closed     instance-terminated-by-user  t3.nano  0.002000    2019-02-24T15:21:36.000Z  sir-r5bh7skq
cancelled  bad-parameters               t3.nano  0.002000    2019-02-24T14:51:47.000Z  sir-1k9s5h3m
closed     instance-terminated-by-user  t3.nano  0.002000    2019-02-24T14:55:26.000Z  sir-43x16b6n
cancelled  bad-parameters               t3.nano  0.002000    2019-02-24T14:29:23.000Z  sir-2jsh5brn
active     fulfilled                    t3.nano  0.002000    2019-02-24T15:37:26.000Z  sir-z1e9591m
cancelled  bad-parameters               t3.nano  0.002000    2019-02-24T14:33:42.000Z  sir-n7c15y5p

Eingang:

$ cat /tmp/ins
{
    "SpotInstanceRequests": [
        {
            "Status": {
                "Message": "2019-02-24T15:29:38+0000 : 2019-02-24T15:29:38+0000 : Spot Instance terminated due to user-initiated termination.", 
                "Code": "instance-terminated-by-user", 
                "UpdateTime": "2019-02-24T15:31:03.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T15:21:36.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "InstanceId": "i-0414083bef5e91d94", 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-r5bh7skq", 
            "State": "closed", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda1", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "public", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T15:21:36.000Z", 
            "SpotPrice": "0.008000"
        }, 
        {
            "Status": {
                "Message": "Your Spot request failed due to bad parameters.", 
                "Code": "bad-parameters", 
                "UpdateTime": "2019-02-24T14:51:48.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T14:51:47.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "Fault": {
                "Message": "Invalid device name /dev/sda", 
                "Code": "InvalidBlockDeviceMapping"
            }, 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-1k9s5h3m", 
            "State": "cancelled", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "public", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T14:51:47.000Z", 
            "SpotPrice": "0.011600"
        }, 
        {
            "Status": {
                "Message": "2019-02-24T15:02:17+0000 : 2019-02-24T15:02:17+0000 : Spot Instance terminated due to user-initiated termination.", 
                "Code": "instance-terminated-by-user", 
                "UpdateTime": "2019-02-24T15:03:34.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T14:55:26.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "InstanceId": "i-010442ac3cc85ec08", 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-43x16b6n", 
            "State": "closed", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda1", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "public", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T14:55:26.000Z", 
            "SpotPrice": "0.011600"
        }, 
        {
            "Status": {
                "Message": "Your Spot request failed due to bad parameters.", 
                "Code": "bad-parameters", 
                "UpdateTime": "2019-02-24T14:29:24.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T14:29:23.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "Fault": {
                "Message": "Addressing type must be 'public'", 
                "Code": "InvalidParameterCombination"
            }, 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-2jsh5brn", 
            "State": "cancelled", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T14:29:23.000Z", 
            "SpotPrice": "0.011600"
        }, 
        {
            "Status": {
                "Message": "Your spot request is fulfilled.", 
                "Code": "fulfilled", 
                "UpdateTime": "2019-02-24T15:37:28.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T15:37:26.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "InstanceId": "i-0a29e9de6d59d433f", 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-z1e9591m", 
            "State": "active", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda1", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "public", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T15:37:26.000Z", 
            "SpotPrice": "0.008000"
        }, 
        {
            "Status": {
                "Message": "Your Spot request failed due to bad parameters.", 
                "Code": "bad-parameters", 
                "UpdateTime": "2019-02-24T14:33:43.000Z"
            }, 
            "ActualBlockHourlyPrice": "0.002000", 
            "ValidUntil": "2019-03-03T14:33:42.000Z", 
            "InstanceInterruptionBehavior": "terminate", 
            "Tags": [], 
            "Fault": {
                "Message": "Invalid device name /dev/sda", 
                "Code": "InvalidBlockDeviceMapping"
            }, 
            "BlockDurationMinutes": 60, 
            "SpotInstanceRequestId": "sir-n7c15y5p", 
            "State": "cancelled", 
            "ProductDescription": "Linux/UNIX", 
            "LaunchedAvailabilityZone": "eu-north-1a", 
            "LaunchSpecification": {
                "Placement": {
                    "Tenancy": "default", 
                    "AvailabilityZone": "eu-north-1a"
                }, 
                "ImageId": "AMI-6d27a913", 
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda", 
                        "VirtualName": "root", 
                        "NoDevice": "", 
                        "Ebs": {
                            "Encrypted": false, 
                            "DeleteOnTermination": true, 
                            "VolumeType": "gp2", 
                            "VolumeSize": 8
                        }
                    }
                ], 
                "EbsOptimized": false, 
                "SecurityGroups": [
                    {
                        "GroupName": "default"
                    }
                ], 
                "Monitoring": {
                    "Enabled": false
                }, 
                "InstanceType": "t3.nano", 
                "AddressingType": "public", 
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0, 
                        "Description": "eth-zero", 
                        "NetworkInterfaceId": "", 
                        "DeleteOnTermination": true, 
                        "SubnetId": "subnet-420ffc2b", 
                        "AssociatePublicIpAddress": true
                    }
                ]
            }, 
            "Type": "one-time", 
            "CreateTime": "2019-02-24T14:33:42.000Z", 
            "SpotPrice": "0.011600"
        }
    ]
}

Wenn die Werte keine Leerzeichen enthalten, kann dies hilfreich sein:

read -r -a data <<<'name1 value1 name2 value2'

echo "name value"
echo "=========="

for ((i=0; i<${#data[@]}; i+=2)); do
  echo ${data[$i]} ${data[$((i+1))]}
done

Ausgabe

name value
==========
name1 value1
name2 value2
0