jq로 JSON 데이터를 변환하는 방법
저자는 Write for DOnations 프로그램을 선택했습니다.
소개
큰 JSON 파일로 작업할 때 필요한 정보를 찾고 조작하기 어려울 수 있습니다. 모든 관련 스니펫을 복사하여 붙여넣어 총계를 수동으로 계산할 수 있지만 이는 시간이 많이 걸리는 프로세스이며 사람이 실수하기 쉽습니다. 또 다른 옵션은 정보를 찾고 조작하기 위한 범용 도구를 사용하는 것입니다. 모든 최신 Linux 시스템에는 grep
의 세 가지 기존 텍스트 처리 유틸리티가 설치되어 있습니다. 이러한 명령은 느슨하게 구조화된 데이터로 작업할 때 유용하지만 JSON과 같은 기계 판독 가능 데이터 형식을 위한 다른 옵션이 있습니다.
명령줄 JSON 처리 도구인 jq
는 기계가 읽을 수 있는 데이터 형식을 처리하기 위한 좋은 솔루션이며 특히 셸 스크립트에서 유용합니다. jq
를 사용하면 데이터를 조작해야 할 때 도움이 될 수 있습니다. 예를 들어 JSON API에 대한 curl
호출을 실행하면 jq
가 서버 응답에서 특정 정보를 추출할 수 있습니다. 데이터 엔지니어로서 데이터 수집 프로세스에 jq
를 통합할 수도 있습니다. Kubernetes 클러스터를 관리하는 경우 kubectl
의 JSON 출력을 jq
의 입력 소스로 사용하여 특정 배포에 사용 가능한 복제본 수를 추출할 수 있습니다.
이 기사에서는 jq
를 사용하여 해양 동물에 대한 샘플 JSON 파일을 변환합니다. 필터를 사용하여 데이터 변환을 적용하고 변환된 데이터 조각을 새로운 데이터 구조로 병합합니다. 자습서가 끝나면 jq
스크립트를 사용하여 조작한 데이터에 대한 질문에 답할 수 있습니다.
전제 조건
이 자습서를 완료하려면 다음이 필요합니다.
- JSON 구문 분석 및 변환 도구인
jq
. 모든 주요 Linux 배포판의 리포지토리에서 사용할 수 있습니다. Ubuntu를 사용하는 경우sudo apt install jq
를 실행하여 설치합니다.- JSON 소개에서 새로 고칠 수 있는 JSON 구문에 대한 이해
1단계 - 첫 번째 jq 명령 실행
이 단계에서는 샘플 입력 파일을 설정하고 jq
명령을 실행하여 샘플 파일 데이터의 출력을 생성하여 설정을 테스트합니다. jq
는 파일이나 파이프에서 입력을 받을 수 있습니다. 전자를 사용하게 됩니다.
샘플 파일을 생성하여 시작합니다. 원하는 편집기를 사용하여 seaCreatures.json
이라는 새 파일을 만들고 엽니다(이 자습서에서는 nano
를 사용함).
- nano seaCreatures.json
다음 내용을 파일에 복사합니다.
[
{ "name": "Sammy", "type": "shark", "clams": 5 },
{ "name": "Bubbles", "type": "orca", "clams": 3 },
{ "name": "Splish", "type": "dolphin", "clams": 2 },
{ "name": "Splash", "type": "dolphin", "clams": 2 }
]
튜토리얼의 나머지 부분에서 이 데이터로 작업하게 됩니다. 자습서가 끝나면 이 데이터에 대한 다음 질문에 답하는 한 줄 jq
명령을 작성하게 됩니다.
- 목록 형식의 바다 생물의 이름은 무엇입니까?
- 생물이 총 몇 마리의 조개를 소유하고 있습니까?
- 돌고래 소유의 조개는 몇 개입니까?
파일을 저장하고 닫습니다.
입력 파일 외에도 JSON 입력을 변경하지 않고 출력으로 전달하는 ID 연산자가 필요합니다.
ID 연산자를 사용하여 설정이 작동하는지 테스트할 수 있습니다. 구문 분석 오류가 표시되면 seaCreatures.json
에 유효한 JSON이 포함되어 있는지 확인하십시오.
다음 명령을 사용하여 ID 연산자를 JSON 파일에 적용합니다.
- jq '.' seaCreatures.json
파일과 함께 jq
를 사용할 때 항상 입력 파일 다음에 필터를 전달합니다. 필터에는 셸에 특별한 의미가 있는 공백 및 기타 문자가 포함될 수 있으므로 필터를 작은따옴표로 묶는 것이 좋습니다. 이렇게 하면 필터가 명령 매개변수임을 쉘에 알립니다. jq
를 실행해도 원본 파일이 수정되지 않으므로 안심하십시오.
다음과 같은 결과가 표시됩니다.
Output[
{
"name": "Sammy",
"type": "shark",
"clams": 5
},
{
"name": "Bubbles",
"type": "orca",
"clams": 3
},
{
"name": "Splish",
"type": "dolphin",
"clams": 2
},
{
"name": "Splash",
"type": "dolphin",
"clams": 2
}
]
기본적으로 jq
는 출력을 예쁘게 인쇄합니다. 자동으로 들여쓰기를 적용하고 모든 값 뒤에 새 줄을 추가하며 가능한 경우 출력에 색상을 지정합니다. 색상을 지정하면 가독성이 향상되어 많은 개발자가 다른 도구에서 생성된 JSON 데이터를 검토할 때 도움이 될 수 있습니다. 예를 들어 JSON API에 curl
요청을 보낼 때 JSON 응답을 jq .
로 파이프하여 예쁘게 인쇄할 수 있습니다.
이제 jq
가 실행되고 있습니다. 입력 파일이 설정되면 creatures
, totalClams
및 totalDolphinClams
. 다음 단계에서는 creatures
값에서 정보를 찾을 수 있습니다.
2단계 - 생물 값 검색
이 단계에서는 이름을 찾기 위해 creatures
값을 사용하여 모든 바다 생물 목록을 생성합니다. 이 단계가 끝나면 다음과 같은 이름 목록이 생성됩니다.
Output[
"Sammy",
"Bubbles",
"Splish",
"Splash"
],
이 목록을 생성하려면 생물의 이름을 추출한 다음 배열로 병합해야 합니다.
모든 생물의 이름을 얻고 나머지는 모두 버리려면 필터를 수정해야 합니다. 배열에 대해 작업하고 있으므로 jq
에 배열 자체가 아니라 해당 배열의 값에 대해 작업하고 싶다고 알려야 합니다. .[]
로 작성된 배열 값 반복자는 이러한 용도로 사용됩니다.
수정된 필터로 jq
를 실행합니다.
- jq '.[]' seaCreatures.json
이제 모든 배열 값이 별도로 출력됩니다.
Output{
"name": "Sammy",
"type": "shark",
"clams": 5
}
{
"name": "Bubbles",
"type": "orca",
"clams": 3
}
{
"name": "Splish",
"type": "dolphin",
"clams": 2
}
{
"name": "Splash",
"type": "dolphin",
"clams": 2
}
모든 배열 항목을 전체로 출력하는 대신 name
속성의 값을 출력하고 나머지는 버릴 수 있습니다. 파이프 연산자 |
를 사용하면 각 출력에 필터를 적용할 수 있습니다. find를 사용한 경우 | xargs
를 사용하여 모든 검색 결과에 명령을 적용하면 이 패턴이 친숙하게 느껴질 것입니다.
JSON 개체의 name
속성은 .name
을 작성하여 액세스할 수 있습니다. 파이프를 필터와 결합하고 seaCreatures.json
에서 다음 명령을 실행합니다.
- jq '.[] | .name' seaCreatures.json
출력에서 다른 속성이 사라진 것을 알 수 있습니다.
Output"Sammy"
"Bubbles"
"Splish"
"Splash"
기본적으로 jq
는 유효한 JSON을 출력하므로 문자열은 큰따옴표(\\
) 안에 표시됩니다. 큰따옴표가 없는 문자열이 필요한 경우 -r
플래그를 추가하여 원시 출력을 활성화합니다.
- jq -r '.[] | .name' seaCreatures.json
따옴표가 사라졌습니다.
OutputSammy
Bubbles
Splish
Splash
이제 JSON 입력에서 특정 정보를 추출하는 방법을 알게 되었습니다. 이 기술을 사용하여 다음 단계에서 다른 특정 정보를 찾은 다음 마지막 단계에서 creatures
값을 생성합니다.
3단계 — map 및 add로 totalClams 값 계산
이 단계에서는 생물이 소유한 조개 수에 대한 총 정보를 찾을 수 있습니다. 몇 가지 데이터를 집계하여 답을 계산할 수 있습니다. jq
에 익숙해지면 수동 계산보다 빠르고 인적 오류가 덜 발생합니다. 이 단계의 끝에서 예상되는 값은 12
입니다.
2단계에서는 항목 목록에서 특정 정보를 추출했습니다. 이 기술을 재사용하여 clams
속성 값을 추출할 수 있습니다. 이 새 속성에 대한 필터를 조정하고 다음 명령을 실행합니다.
- jq '.[] | .clams' seaCreatures.json
clams
속성의 개별 값이 출력됩니다.
Output5
3
2
2
개별 값의 합계를 찾으려면 add
필터가 필요합니다. add
필터는 배열에서 작동합니다. 그러나 현재 배열 값을 출력하고 있으므로 먼저 배열로 래핑해야 합니다.
다음과 같이 기존 필터를 []
로 묶습니다.
- jq '[.[] | .clams]' seaCreatures.json
값이 목록에 나타납니다.
Output[
5,
3,
2,
2
]
add
필터를 적용하기 전에 map
기능을 사용하여 명령의 가독성을 향상시킬 수 있으며 유지 관리도 더 쉬워집니다. 하나의 map
호출로 배열을 반복하고 각 항목에 필터를 적용한 다음 결과를 배열로 래핑할 수 있습니다. 항목 배열이 주어지면 map
은 인수를 각 항목에 대한 필터로 적용합니다. 예를 들어 map(.name)
필터를 [{\name\: \Sammy\}에 적용하면 { name: Bubbles}]
, 결과 JSON 개체는 [Sammy, Bubbles]
입니다.
대신 map
함수를 사용하도록 배열을 생성하도록 필터를 다시 작성한 다음 실행합니다.
- jq 'map(.clams)' seaCreatures.json
이전과 동일한 출력을 받게 됩니다.
Output[
5,
3,
2,
2
]
이제 배열이 있으므로 add
필터로 파이프할 수 있습니다.
- jq 'map(.clams) | add' seaCreatures.json
배열의 합계를 받게 됩니다.
Output12
이 필터를 사용하여 나중에 totalClams
값을 생성하는 데 사용할 총 조개 수를 계산했습니다. 세 가지 질문 중 두 가지에 대한 필터를 작성했습니다. 생성할 필터가 하나 더 있으며 그 후에 최종 출력을 생성할 수 있습니다.
4단계 - 추가 필터로 totalDolphinClams 값 계산
이제 생물이 얼마나 많은 조개를 소유하고 있는지 알았으므로 돌고래가 얼마나 많은 조개를 가지고 있는지 식별할 수 있습니다. 특정 조건을 만족하는 배열 요소의 값만 추가하여 답을 생성할 수 있습니다. 이 단계의 끝에서 예상되는 값은 4
이며 돌고래가 가지고 있는 총 조개 수입니다. 마지막 단계에서 결과 값은 totalDolphinClams
속성에서 사용됩니다.
3단계에서 했던 것처럼 모든 조개
값을 추가하는 대신 \돌고래\
유형의 생물이 보유한 조개만 계산합니다. select(condition)
와 같이 select
함수를 사용하여 특정 조건을 선택합니다. 조건이 true
로 평가되는 모든 입력이 전달됩니다. 다른 모든 입력은 무시됩니다. 예를 들어 JSON 입력이 \dolphin\
이고 필터가 select(. == \dolphin\)
인 경우 출력은 \dolphin\입니다.
. 입력 \Sammy\
의 경우 동일한 필터가 아무 것도 출력하지 않습니다.
배열의 모든 값에 select
를 적용하려면 map
과 쌍을 이룰 수 있습니다. 이때 조건을 만족하지 않는 배열 값은 폐기됩니다.
귀하의 경우 type
값이 \dolphin\
인 배열 값만 유지하려고 합니다. 결과 필터는 다음과 같습니다.
- jq 'map(select(.type == "dolphin"))' seaCreatures.json
필터는 상어 Sammy 및 범고래 Bubbles와 일치하지 않지만 두 마리의 돌고래와 일치합니다.
Output[
{
"name": "Splish",
"type": "dolphin",
"clams": 2
},
{
"name": "Splash",
"type": "dolphin",
"clams": 2
}
]
이 출력에는 생물당 조개 수와 관련 없는 일부 정보가 포함됩니다. clams
값만 유지하려면 map
의 매개변수 끝에 필드 이름을 추가할 수 있습니다.
- jq 'map(select(.type == "dolphin").clams)' seaCreatures.json
map
함수는 배열을 입력으로 받고 map
의 필터(인수로 전달됨)를 각 배열 요소에 적용합니다. 결과적으로 select
는 생물당 한 번씩 총 4번 호출됩니다. select
함수는 두 돌고래에 대한 출력을 생성하고(조건과 일치하므로) 나머지는 생략합니다.
출력은 일치하는 두 생물의 clams
값만 포함하는 배열이 됩니다.
Output[
2,
2
]
배열 값을 add
로 파이프합니다.
- jq 'map(select(.type == "dolphin").clams) | add' seaCreatures.json
출력은 \돌고래\
유형의 생물에서 조개
값의 합계를 반환합니다.
Output4
map
과 select
를 성공적으로 결합하여 배열에 액세스하고, 조건과 일치하는 배열 항목을 선택하고, 변환하고, 해당 변환의 결과를 합산했습니다. 이 전략을 사용하여 최종 출력에서 totalDolphinClams
를 계산할 수 있습니다. 이 작업은 다음 단계에서 수행합니다.
5단계 - 데이터를 새로운 데이터 구조로 변환
이전 단계에서 필터를 작성하여 샘플 데이터를 추출하고 조작했습니다. 이제 이러한 필터를 결합하여 데이터에 대한 질문에 답하는 출력을 생성할 수 있습니다.
- 목록 형식의 바다 생물의 이름은 무엇입니까?
- 생물이 총 몇 마리의 조개를 소유하고 있습니까?
- 돌고래 소유의 조개는 몇 개입니까?
목록 형식으로 바다 생물의 이름을 찾기 위해 map
함수를 사용했습니다: map(.name)
. 생물이 총 몇 개의 조개를 소유하고 있는지 알아보기 위해 모든 clams
값을 add
필터에 연결했습니다. map(.clams) | 추가
. 돌고래가 소유한 조개의 수를 알아보기 위해 .type == \dolphin\
조건과 함께 select
함수를 사용했습니다. map(select(. type == 돌고래).clams) | 추가
.
이러한 필터를 모든 작업을 수행하는 하나의 jq
명령으로 결합합니다. 원하는 정보를 표시하는 새 데이터 구조를 만들기 위해 세 가지 필터를 병합하는 새 JSON 개체를 만듭니다.
참고로 시작 JSON 파일은 다음과 일치합니다.
[
{ "name": "Sammy", "type": "shark", "clams": 5 },
{ "name": "Bubbles", "type": "orca", "clams": 3 },
{ "name": "Splish", "type": "dolphin", "clams": 2 },
{ "name": "Splash", "type": "dolphin", "clams": 2 }
]
변환된 JSON 출력은 다음을 생성합니다.
Final Output{
"creatures": [
"Sammy",
"Bubbles",
"Splish",
"Splash"
],
"totalClams": 12,
"totalDolphinClams": 4
}
다음은 입력 값이 비어 있는 전체 jq
명령의 구문 데모입니다.
- jq '{ creatures: [], totalClams: 0, totalDolphinClams: 0 }' seaCreatures.json
이 필터를 사용하여 세 가지 속성을 포함하는 JSON 객체를 생성합니다.
Output{
"creatures": [],
"totalClams": 0,
"totalDolphinClams": 0
}
최종 출력처럼 보이기 시작했지만 seaCreatures.json
파일에서 가져오지 않았기 때문에 입력 값이 올바르지 않습니다.
하드 코딩된 속성 값을 각 이전 단계에서 만든 필터로 바꿉니다.
- jq '{ creatures: map(.name), totalClams: map(.clams) | add, totalDolphinClams: map(select(.type == "dolphin").clams) | add }' seaCreatures.json
위의 필터는 jq
에게 다음을 포함하는 JSON 개체를 생성하도록 지시합니다.
- 모든 생물의
이름
값 목록을 포함하는생물
속성 - 모든 생물의
clams
값의 합계를 포함하는totalClams
속성입니다. type
이\dolphin\
인 모든 생물의clams
값의 합계를 포함하는totalDolphinClams
속성입니다.
명령을 실행하면 이 필터의 출력은 다음과 같아야 합니다.
Output{
"creatures": [
"Sammy",
"Bubbles",
"Splish",
"Splash"
],
"totalClams": 12,
"totalDolphinClams": 4
}
이제 세 가지 질문 모두에 대한 관련 데이터를 제공하는 단일 JSON 개체가 있습니다. 데이터 세트가 변경되면 작성한 jq
필터를 사용하여 언제든지 변환을 다시 적용할 수 있습니다.
결론
JSON 입력으로 작업할 때 jq
는 sed
와 같은 텍스트 조작 도구로는 어려울 수 있는 다양한 데이터 변환을 수행하는 데 도움이 될 수 있습니다. 이 자습서에서는 select
함수를 사용하여 데이터를 필터링하고 map
을 사용하여 배열 요소를 변환하고 add
필터를 사용하여 숫자 배열을 합산하고 학습했습니다. 변환을 새 데이터 구조로 병합하는 방법.
jq
고급 기능에 대해 알아보려면 모든 형식에서 작동하는 텍스트 처리 기술에 대한 정보를 위해 grep
을 자세히 살펴보십시오.