jq를 사용하여 JSON 문자열을 테이블로 포맷하려면 어떻게 해야 합니까?
방금 Bash 스크립팅으로 시작했는데 JSON에서 작업하는 jq를 우연히 발견했습니다.
다음과 같은 JSON 문자열을 단말기에 출력하기 위한 테이블로 변환해야 합니다.
[{
"name": "George",
"id": 12,
"email": "george@domain.example"
}, {
"name": "Jack",
"id": 18,
"email": "jack@domain.example"
}, {
"name": "Joe",
"id": 19,
"email": "joe@domain.example"
}]
단말기에 표시할 내용:
ID Name
=================
12 George
18 Jack
19 Joe
각 행의 전자 메일 속성을 표시하지 않기 때문에 jq 명령에는 필터링이 필요합니다.다음은 이름과 ID의 간단한 목록을 제공합니다.
list=$(echo "$data" | jq -r '.[] | .name, .id')
printf "$list"
문제는 테이블처럼 표시할 수 없다는 것입니다.jq에 포맷 옵션이 몇 가지 있는 것은 알지만, 사용 시 사용할 수 있는 옵션에는 미치지 못합니다.printf
이 값들을 배열로 해서 스스로 루프해서 포맷을 할 수 있도록 하고 싶습니다.내가 시도했던 것들은 나에게 다양한 결과를 가져다 주지만, 내가 진정으로 원하는 것은 결코 아니다.
누가 나를 올바른 방향으로 인도해 줄 수 있나요?
사용방법@tsv
필터는 주로 표준적인 방법으로 수많은 "엣지 케이스"를 처리하기 때문에 많은 권장 사항이 있습니다.
.[] | [.id, .name] | @tsv
다음과 같이 헤더를 추가할 수 있습니다.
jq -r '["ID","NAME"], ["--","------"], (.[] | [.id, .name]) | @tsv'
그 결과:
ID NAME
-- ------
12 George
18 Jack
19 Joe
@Tobia에서 지적한 바와 같이 표를 포맷하여 표시할 수 있습니다.column
jq에 의해 생성된 결과를 후처리합니다.bash와 같은 셸을 사용하는 경우column -ts $'\t'
꽤 휴대하기 쉬울 겁니다.
length*"-"
대시 라인 생성을 자동화하려면 다음 절차를 수행합니다.
jq -r '(["ID","NAME"] | (., map(length*"-"))), (.[] | [.id, .name]) | @tsv'
다음과 같은 것이 있습니다.
echo '[{
"name": "George",
"id": 12,
"email": "george@domain.example"
}, {
"name": "Jack",
"id": 18,
"email": "jack@domain.example"
}, {
"name": "Joe",
"id": 19,
"email": "joe@domain.example"
}]' | jq -r '.[] | "\(.id)\t\(.name)"'
산출량
12 George
18 Jack
19 Joe
Edit 1 : 세밀한 포맷에는 다음과 같은 도구를 사용합니다.awk
echo '[{
"name": "George",
"id": 12,
"email": "george@domain.example"
}, {
"name": "Jack",
"id": 18,
"email": "jack@domain.example"
}, {
"name": "Joe",
"id": 19,
"email": "joe@domain.example"
}]' | 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 : 응답
jq에서 직접 배열을 포함하는 변수를 가져올 수 있는 방법은 없나요?
왜 안 되나요?
E-메일이 어레이로 변경되는 (실제로 고객님의 것을 수정한) 예를 보면 알 수 있습니다.
echo '[{
"name": "George",
"id": 20,
"email": [ "george@domain1.example" , "george@domain2.example" ]
}, {
"name": "Jack",
"id": 18,
"email": [ "jack@domain3.example" , "jack@domain5.example" ]
}, {
"name": "Joe",
"id": 19,
"email": [ "joe@domain.example" ]
}]' | jq -r '.[] | .email'
산출량
[
"george@domain1.example",
"george@domain2.example"
]
[
"jack@domain3.example",
"jack@domain5.example"
]
[
"joe@domain.example"
]
손으로 헤더를 정의하는 것은 차선책입니다!헤더를 생략하는 것도 차선책입니다.
TL;DR
데이터.
[{ "name": "George", "id": 12, "email": "george@domain.example" },
{ "name": "Jack", "id": 18, "email": "jack@domain.example" },
{ "name": "Joe", "id": 19, "email": "joe@domain.example" }]
대본
[.[]| with_entries( .key |= ascii_downcase ) ]
| (.[0] |keys_unsorted | @tsv)
, (.[] |map(.) |@tsv)
달리는 법
$ < data jq -rf script | column -t
name id email
George 12 george@domain.example
Jack 18 jack@domain.example
Joe 19 joe@domain.example
아마존 웹 서비스의 데이터를 정리하던 중 이 질문을 발견했습니다.다른 예를 원하실 경우를 대비해서 제가 해결하던 문제:
$ 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
출력:
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
입력:
$ 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"
}
]
}
위의 답변의 문제는 필드가 모두 같은 너비일 경우에만 유효하다는 것입니다.
이 문제를 방지하려면 Linuxcolumn
명령어를 사용할 수 있습니다.
// input.json
[
{
"name": "George",
"id": "a very very long field",
"email": "george@domain.example"
},
{
"name": "Jack",
"id": 18,
"email": "jack@domain.example"
},
{
"name": "Joe",
"id": 19,
"email": "joe@domain.example"
}
]
그 후, 다음과 같이 입력합니다.
▶ jq -r '.[] | [.id, .name] | @tsv' input.json | column -ts $'\t'
a very very long field George
18 Jack
19 Joe
이 모든 행동을 취하기 위해 모든 반응을 혼합했습니다.
- 헤더 테이블 작성
- 긴 밭을 다루다
- 재사용할 수 있는 기능을 만들다
함수 배시
function jsonArrayToTable(){
jq -r '(.[0] | ([keys[] | .] |(., map(length*"-")))), (.[] | ([keys[] as $k | .[$k]])) | @tsv' | column -t -s $'\t'
}
사용 예
echo '[{"key1":"V1.1", "key2":"V2.1"}, {"keyA":"V1.2", "key2":"V2.2"}]' | jsonArrayToTable
산출량
key1 key2
---- ----
V1.1 V2.1
V2.2 V1.2
터미널 출력 테이블 대신 HTML 테이블을 생성하는 경우:
echo '[{
"name": "George",
"id": 12,
"email": "george@domain.example"
}, {
"name": "Jack",
"id": 18,
"email": "jack@domain.example"
}, {
"name": "Joe",
"id": 19,
"email": "joe@domain.example"
}]' | jq -r 'map("<tr><td>" + .name + "</td><td>" + (.id | tostring) + "</td></tr>") | ["<table>"] + . + ["</table>"] | .[]'
출력:
<table>
<tr><td>George</td><td>12</td></tr>
<tr><td>Jack</td><td>18</td></tr>
<tr><td>Joe</td><td>19</td></tr>
</table>
값에 공백이 포함되어 있지 않은 경우 다음과 같이 하면 도움이 될 수 있습니다.
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
산출량
name value
==========
name1 value1
name2 value2
보다 심플한 구현:
jq -r '(.[0]|keys_unsorted|(.,map(length*"-"))),.[]|map(.)|@tsv'|column -ts $'\t'
하다 jq에 다음 함수를 할 수 .~/.jq
:
def pretty_table:
(.[0]|keys_unsorted|(.,map(length*"-"))),.[]|map(.)|@tsv
;
다음 명령을 실행합니다.
cat apps.json | jq -r pretty_table | column -ts $'\t'
언급URL : https://stackoverflow.com/questions/39139107/how-to-format-a-json-string-as-a-table-using-jq
'programing' 카테고리의 다른 글
.htaccess를 사용하여 HTTP를 HTTP로 리다이렉트합니다. (0) | 2023.02.25 |
---|---|
Lombok을 사용한 Spring Boot 로깅 (0) | 2023.02.25 |
Angular JS는 DOM의 변화를 감시 (0) | 2023.02.25 |
스프링 부트 메이븐 플러그인 클래스를 찾을 수 없습니다. (0) | 2023.02.25 |
디스플레이 시뮬레이션: React Native 인라인 (0) | 2023.02.25 |