jq를 사용하여 JSON 문자열을 테이블로 포맷하는 방법은 무엇입니까?


83

방금 Bash 스크립팅으로 시작하여 JSON으로 작업하기 위해 jq를 우연히 발견했습니다.

터미널에서 출력하기 위해 아래와 같은 JSON 문자열을 테이블로 변환해야합니다.

[{
    "name": "George",
    "id": 12,
    "email": "george@domain.com"
}, {
    "name": "Jack",
    "id": 18,
    "email": "jack@domain.com"
}, {
    "name": "Joe",
    "id": 19,
    "email": "joe@domain.com"
}]

터미널에 표시 할 내용 :

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

각 행에 대해 이메일 속성을 표시하지 않으려는 경우 jq 명령에 일부 필터링이 포함되어야합니다. 다음은 이름과 ID의 일반 목록을 제공합니다.

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

문제는 표처럼 표시 할 수 없다는 것입니다. jq에는 몇 가지 형식 지정 옵션이 있지만을 사용할 때 사용하는 옵션만큼 좋지는 않습니다 printf. 이 값을 배열로 가져 와서 형식화를 수행 할 수 있습니다 ...? 내가 시도한 것들은 나에게 다양한 결과를 제공하지만 내가 정말로 원하는 것은 결코 아닙니다.

누군가 나를 올바른 방향으로 가리킬 수 있습니까?


jq -r ...명령의 샘플 출력을 추가 할 수 있습니까?
Micha Wiedenmann

또는을 사용 echo하지 않을 수 있습니다 . jq -r '...' <<<$datajr -r '...' < input-file.json
Micha Wiedenmann

질문이 있습니까? 문자열이 있습니다. "name1 value1 name2 value2 name3 value3"어떻게 표로 인쇄 할 수 있습니까?
Micha Wiedenmann

답변:


73

다음과 같은 이유는 무엇입니까?

echo '[{
    "name": "George",
    "id": 12,
    "email": "george@domain.com"
}, {
    "name": "Jack",
    "id": 18,
    "email": "jack@domain.com"
}, {
    "name": "Joe",
    "id": 19,
    "email": "joe@domain.com"
}]' | jq -r '.[] | "\(.id)\t\(.name)"'

산출

12  George
18  Jack
19  Joe

편집 1 : 세밀한 서식 지정을 위해 다음과 같은 도구를 사용하십시오.awk

 echo '[{
    "name": "George",
    "id": 12,
    "email": "george@domain.com"
}, {
    "name": "Jack",
    "id": 18,
    "email": "jack@domain.com"
}, {
    "name": "Joe",
    "id": 19,
    "email": "joe@domain.com"
}]' | 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"

편집 2 : 회신

jq에서 직접 배열을 포함하는 변수를 얻을 수있는 방법이 없습니까?

왜 안돼?

이메일이 배열로 변경된 약간 관련된 예제 (사실 당신의 것에서 수정 됨)는 이것을 보여줍니다

echo '[{
    "name": "George",
    "id": 20,
    "email": [ "george@domain1.com" , "george@domain2.com" ]
}, {
    "name": "Jack",
    "id": 18,
    "email": [ "jack@domain3.com" , "jack@domain5.com" ]
}, {
    "name": "Joe",
    "id": 19,
    "email": [ "joe@domain.com" ]
}]' | jq -r '.[] | .email'

산출

[
  "george@domain1.com",
  "george@domain2.com"
]
[
  "jack@domain3.com",
  "jack@domain5.com"
]
[
  "joe@domain.com"
]

답변 주셔서 감사합니다. 이것은이 특별한 경우에 아주 잘 작동합니다. id의 길이는 모두 같습니다. 필드 순서를 바꾸면 편리한 테이블처럼 보이지 않는 무언가를 얻을 수 있다고 상상해보십시오. 더 많은 데이터 세트에서 사용할 수있는 솔루션을 찾고 있습니다. 답변 해주셔서 감사합니다!
Rein

알겠습니다. jq에서 직접 배열을 포함하는 변수를 얻을 수있는 방법이 없습니까? 나는 항상 문자열에서 가야합니까?
Rein

도움을 주셔서 감사합니다. 출력은 내가 원했던 그대로입니다 (이름 주위에 인용 부호 제외). 예를 들어 Python 에서처럼 배열을 사용할 준비를하는 대신 문자열에서 이동하는 것이 이상하게 느껴졌습니다. 나에게 그것은 서투르고 더러워진 것처럼 느껴지지만 bash의 아이디어에 익숙해 져야 할 것은 나 뿐이라고 생각합니다. 이것을 재사용 할 수있는 함수로 만들려고 노력할 것이므로 다른 헤더를 가진 더 많은 JSON 문자열에 사용할 수 있습니다.
Rein

@Rein : 세분화 된 형식의 경우 출력을 csv 형식으로 인쇄 한 다음을 사용해야 awk하지만 복잡한 경우는 실패 할 수 있습니다. 두 번째 의견에 대해서는 마지막 편집 내용을보고 [this] 답변 과 함께 읽으십시오 .
sjsam

1
링크와 설명에 감사드립니다. 매우 유용합니다!
Rein

96

@tsv필터를 사용하면 권장 할 사항이 많습니다. 주로 표준 방식으로 수많은 "에지 케이스"를 처리하기 때문입니다.

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

헤더 추가는 다음과 같이 수행 할 수 있습니다.

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

결과:

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

length*"-"

대시 라인 생산을 자동화하려면 :

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

@tsv 필터는 jq, hmm의 기본 필터 메뉴얼 페이지에도 있습니다. 내가 놓친 다른 것이 있는지 궁금합니다. :)
Ярослав Рахматуллин

17

헤더를 직접 정의하는 것은 차선책입니다! 헤더를 생략하는 것도 차선책입니다.

TL; DR

데이터

[{ "name": "George", "id": 12, "email": "george@domain.com" },
{ "name": "Jack", "id": 18, "email": "jack@domain.com" }, 
{ "name": "Joe", "id": 19, "email": "joe@domain.com" }]

스크립트

  [.[]| with_entries( .key |= ascii_downcase ) ]
      |    (.[0] |keys_unsorted | @tsv)
         , (.[]|.|map(.) |@tsv)

달리는 방법

$ < data jq -rf script  | column -t
name    id  email
George  12  george@domain.com
Jack    18  jack@domain.com
Joe     19  joe@domain.com

아마존 웹 서비스의 일부 데이터를 요약하는 동안이 질문을 발견했습니다. 다른 예를 원할 경우 내가 작업하고 있던 문제 :

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

1
column -t헤더를 테이블 자체에 맞추는 트릭을 만들었습니다. 감사!
Dimitris Moraitidis 20.01.14

를 사용 column -ts $'\t'하여 공백이 아닌 탭 문자로 분할 할 수 있습니다. 그렇지 않으면 공백이있는 값이 여러 열로 분할됩니다. 에서 unix.stackexchange.com/a/57235/140650
alexanderbird

0

값에 공백이없는 경우 다음이 도움이 될 수 있습니다.

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에서 바로 배열을 얻을 수 없다는 것을 깨닫기 시작했습니다. 맞습니까? 그래서 갈 방법은 (실행 가능한 형식으로) 문자열을 가져 와서 거기에서가는 것입니까?
Rein

0

위 답변의 문제는 필드가 모두 동일한 너비 인 경우에만 작동한다는 것입니다.

이 문제를 방지하기 위해 Linux column명령을 사용할 수 있습니다.

// input.json
[
  {
    "name": "George",
    "id": "a very very long field",
    "email": "george@domain.com"
  },
  {
    "name": "Jack",
    "id": 18,
    "email": "jack@domain.com"
  },
  {
    "name": "Joe",
    "id": 19,
    "email": "joe@domain.com"
  }
]

그때:

▶ jq -r '.[] | [.id, .name] | @tsv' input.json | column -ts $'\t'
a very very long field  George
18                      Jack
19                      Joe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.