Właśnie zacząłem od skryptów Bash i natknąłem się na jq do pracy z JSON.
Muszę przekształcić ciąg JSON, jak poniżej, na tabelę do wyjścia w terminalu.
[{
"name": "George",
"id": 12,
"email": "[email protected]"
}, {
"name": "Jack",
"id": 18,
"email": "[email protected]"
}, {
"name": "Joe",
"id": 19,
"email": "[email protected]"
}]
Co chcę wyświetlić w terminalu:
ID Name
=================
12 George
18 Jack
19 Joe
Zwróć uwagę, że nie chcę wyświetlać właściwości e-mail dla każdego wiersza, więc polecenie jq powinno obejmować pewne filtrowanie. Poniżej znajduje się zwykła lista nazwisk i identyfikatorów:
list=$(echo "$data" | jq -r '.[] | .name, .id')
printf "$list"
Problem w tym, że nie mogę wyświetlić tego jak tabeli. Wiem, że jq ma pewne opcje formatowania, ale nie są tak dobre, jak opcje, które mam podczas używania printf
. Myślę, że chcę uzyskać te wartości w tablicy, którą mogę następnie zapętlić, aby wykonać formatowanie ...? Rzeczy, których próbowałem, dają mi różne rezultaty, ale nigdy tego, czego naprawdę chcę.
Czy ktoś może wskazać mi właściwy kierunek?
jq -r ...
polecenia?echo
Możesz uniknąć używaniajq -r '...' <<<$data
lubjr -r '...' < input-file.json
."name1 value1 name2 value2 name3 value3"
jak mogę wydrukować go jako tabelę?Odpowiedzi:
Dlaczego nie coś takiego:
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)"'
Wynik
Edycja 1: Do formatowania drobnoziarnistego użyj narzędzi takich jak
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"
Edycja 2: W odpowiedzi na
Dlaczego nie?
Dowodem na to jest nieco zawiły przykład (w rzeczywistości zmodyfikowany z twojego), w którym e-mail jest zmieniany na tablicę
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'
Wynik
[ "[email protected]", "[email protected]" ] [ "[email protected]", "[email protected]" ] [ "[email protected]" ]
źródło
awk
, ale pamiętaj, że skomplikowane przypadki mogą zawieść. Jako drugi komentarz zobacz ostatnią edycję i przeczytaj ją razem z [tą] odpowiedzią.Korzystanie z
@tsv
filtra ma wiele do polecania, głównie dlatego, że obsługuje on wiele „przypadków skrajnych” w standardowy sposób:Dodanie nagłówków można wykonać w następujący sposób:
jq -r '["ID","NAME"], ["--","------"], (.[] | [.id, .name]) | @tsv'
Wynik:
length*"-"
Aby zautomatyzować produkcję linii myślników:
jq -r '(["ID","NAME"] | (., map(length*"-"))), (.[] | [.id, .name]) | @tsv'
źródło
Ręczne definiowanie nagłówków jest nieoptymalne! Pomijanie nagłówków jest również nieoptymalne.
TL; DR
dane
[{ "name": "George", "id": 12, "email": "[email protected]" }, { "name": "Jack", "id": 18, "email": "[email protected]" }, { "name": "Joe", "id": 19, "email": "[email protected]" }]
scenariusz
jak biegać
Znalazłem to pytanie, podsumowując niektóre dane z usług internetowych Amazon. Problem, nad którym pracowałem, na wypadek, gdybyś chciał inny przykład:
$ 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
Wynik:
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
Wejście:
$ 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" } ] }
źródło
column -t
podjął sztuczkę, aby wyrównać nagłówki z samą tabelą. Dzięki!column -ts $'\t'
aby podzielić na znaki tabulacji, ale nie spacje - w przeciwnym razie wartości ze spacjami zostaną podzielone na wiele kolumn. Z unix.stackexchange.com/a/57235/140650Jeśli wartości nie zawierają spacji, może to być pomocne:
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
Wynik
źródło
Problem z odpowiedziami powyżej polega na tym, że działają one tylko wtedy, gdy wszystkie pola mają mniej więcej taką samą szerokość.
Aby uniknąć tego problemu,
column
można użyć polecenia Linux :// input.json [ { "name": "George", "id": "a very very long field", "email": "[email protected]" }, { "name": "Jack", "id": 18, "email": "[email protected]" }, { "name": "Joe", "id": 19, "email": "[email protected]" } ]
Następnie:
▶ jq -r '.[] | [.id, .name] | @tsv' input.json | column -ts $'\t' a very very long field George 18 Jack 19 Joe
źródło