JSON 문법과 데이터 타입에 익숙하다고 가정할게요.
jq는 들어오는 JSON 데이터를 단일 표현식(_필터의 파이프라인_으로 작성)에 통과시켜 원하는 형태로 변환된 데이터를 얻는 방식으로 동작해요.
jq 언어는 jq _프로그램_으로 구현되어 있어요.
이 프로그램은 입력이 어떻게 처리되고 출력이 어떻게 표시될지 제어하는 몇 가지 편리한 명령줄 옵션을 제공해요.
아래 예제에서 이런 옵션을 만나게 돼요:
-n 또는 --null-input
보통은 jq 프로그램에 읽을 파일을 주거나, 입력으로 데이터를 보내요.
--null-input 옵션을 사용하면 아무 입력 없이 JSON 데이터를 생성할 수 있어요.
-c 또는 --compact-output
jq는 기본적으로 출력을 보기 좋게 정렬해서 출력해요.
데이터가 보기 좋게 서식이 맞춰져 있으면 사람이 보기에 아주 유용해요.
하지만 기계에게는 그럴 필요가 없어요. --compact-output 옵션은 서식용 공백을 제거해서 결과 JSON의 크기를 줄여줘요.
-f filename 또는 --from-file filename
명령줄에서 직접 주는 대신 filename에서 jq 프로그램을 읽어요.
sed와 awk도 같은 목적으로 -f 옵션을 사용해요.
연습 문제의 테스트 스크립트에서 이 옵션이 사용되는 것을 볼 수 있어요.
이 강의의 나머지 부분에서는 jq 언어 자체에 집중할게요.
필터는 표현식이라고도 해요.
_필터_는 입력을 받아 출력을 만들어요.
유닉스 셸에서 작업하는 방식처럼, 파이프 |로 _필터_를 이어서 한 _필터_의 출력을 다른 _필터_의 입력으로 연결할 수 있어요.
.
가장 단순한 _필터_예요. 입력을 그대로 출력으로 넘겨줄 뿐이에요.
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
배열을 다루는 방법을 빠르게 소개할게요. 이 주제는 나중에 더 자세히 다뤄요.
_배열_의 원소는 대괄호로 접근하며, 인덱스는 0부터 시작해요.
$ echo '[10, 20, 30]' | jq '.[1]'
20
_필터_는 표현식을 [와 ]로 감싸서 _배열_을 만들 수 있어요.
알려진 원소 목록으로 만드는 경우:
jq -n '[1, 2, 3]'
원소의 스트림을 모으는 경우: 예를 들어,
range는 숫자 스트림을 출력하는 함수예요
$ jq -n 'range(10; 70; 15)'
10
25
40
55
[]를 사용하면 표현식의 결과를 배열로 모아요
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
_쉼표_는 배열 원소를 구분하는 문법만이 아니에요. 쉼표는 스트림을 이어주는 연산자예요.
예를 들어 [1, 2, 3]은 1, 2, 3 세 표현식을 이어붙인 결과를 모으기 위해 배열 생성자 []를 사용하는 _필터_예요.
위의 range(10; 70; 15)에서 세미콜론을 눈치챘나요?
jq 언어에서 _쉼표_는 특정한 용도가 있기 때문에, 여러 인자를 받는 함수는 인자를 구분할 때 세미콜론을 사용해요.
객체를 빠르게 소개할게요.
많은 프로그래밍 언어와 비슷하게, 점(.)으로 객체 속성에 접근해요.
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
_객체_에도 대괄호를 사용할 수 있지만, 그럴 때는 문자열 리터럴에 따옴표가 필요해요. 공백이 들어간 키를 다루는 한 가지 방법이에요.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
{}와 key: value 쌍으로 _객체_를 만들 수 있어요.
"단순한" 문자열인 키 주위에는 따옴표가 필요 없어요.
jq -n '{question: (6 * 9), answer: 42}'
는 다음을 출력해요
{
"question": 54,
"answer": 42
}
_키_를 _표현식_으로 다루려면 괄호로 감싸야 해요 (다음도 위와 같은 결과를 출력해요).
echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'
예를 들어, 다음 내용이 담긴 file.json이 있다고 해요
{
"key1": "value1",
"key2": [5, 15, 25]
}
key2 _배열_의 길이를 계산해볼까요:
$ jq '.key2 | length' file.json
3
.key2 표현식의 출력을 length의 입력으로 _파이프_하고 있어요. 그러면 예상대로 _배열_의 원소 개수를 출력해요.
이 예제에서는 입력 JSON 데이터가 무시되고 출력에 아무 영향도 주지 않아요:
$ echo '{"answer": 42}' | jq '6 * 9'
54
_필터_는 값을 하나 이상 출력할 수 있어요.
예를 들어 .[] _필터_는 _배열_의 각 원소를 별개의 값으로 출력해요:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
이런 _필터_를 다른 필터로 파이프하면 두 번째 _필터_가 각 값마다 실행돼요:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
암시적 반복과 비슷해요.
이 기법을 이해하고 나면, 아주 강력한 jq _필터_를 아주 간결하게 작성할 수 있다는 걸 알게 될 거예요.
괄호는 다른 언어에서와 마찬가지로 연산 순서를 강제하기 위해 하위 표현식을 묶는 데 사용해요.
jq에서는 이 괄호가 필요해 보이는 상황이 다소 의외로 느껴질 수 있어요.
예를 들어, 원소가 두 개인 _배열_을 만들고 싶다고 해요. 9의 제곱근, 그리고 _e_의 1제곱이에요.
두 개별 표현식은 9 | sqrt와 1 | exp예요.
기대하는 출력은 배열 [3, 2.7...]이에요.
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
왜 기대한 결과를 얻지 못했을까요? jq는 저 식을 이렇게 해석해요:
[ ((9|sqrt), 1) | exp ]
jq는 두 원소(3과 1)의 스트림을 만들고, 각각을 exp에 넘겨요.
exp가 숫자 하나만 입력으로 받도록 해야 해요.
다시 말해, 쉼표보다 파이프가 먼저 평가되도록 강제해야 해요.
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
깊이 들어가지는 않고(함수는 다른 연습 문제에서 다룰 주제예요), 유용한 내장 함수 몇 가지를 소개할게요:
length
배열을 입력으로 받으면 배열의 원소 개수를 출력해요.
$ jq -n '[10, 20, 30, 40] | length'
4
+
이 연산자는 피연산자의 타입에 따라 하는 일이 달라요. 숫자는 더하고, 문자열은 이어붙이고, 배열은 덧붙이고, 객체는 병합해요.
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add는 배열을 받아 모든 원소를 +의 규칙으로 더한 항목을 반환하는 함수예요.
[1, 2, 3] | add는 6을 출력해요.
map
배열을 입력으로, 필터를 인자로 받아, 각 원소에 필터를 적용한 배열을 출력해요
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
select
어떤 입력과 필터를 인자로 받아요:
null 값이 아니라, 정말 아무 출력도 없어요)예를 들어, 어떤 숫자들 중에서 3으로 나누어 떨어지는 것을 골라봐요
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
range는 숫자의 _스트림_을 출력한다는 걸 기억해요.
select는 각 숫자마다 한 번씩 호출돼요.
표현식을 "통과"하는 숫자만 출력돼요.
배열의 원소를 골라내야 할 때가 자주 있어요. 이를 위한 방법이 몇 가지 있어요.
입력이 ["Anne", "Bob", "Cathy", "Dave"]일 때, 길이가 4인 이름을 골라봐요.
map과 select를 함께 사용하기
map(select(length == 4))
배열을 원소로 펼치고, 그 스트림에 select를 적용한 뒤, 결과를 모으기
[ .[] | select(length == 4) ]
주석은 # 문자로 시작해서 줄 끝까지 이어져요.
문득 팬케이크가 먹고 싶어졌어요. 기술에 관심이 많은 현대인답게, 팬케이크 재료 쇼핑 목록을 JSON 형식으로 작성해요. 목록의 구조는 다음과 같아요:
{
"name": "name of shopping list",
"ingredients": [ ...list of ingredients... ],
"optional ingredients": [ ...list of ingredients... ]
}
재료는 다음과 같이 JSON 객체로 표현해요:
{
"item": "flour",
"amount": {
"quantity": 2,
"unit": "cup"
}
}
재료에는 "substitute"라는 선택적 속성이 있을 수 있는데, 여기에는 대신 사용할 수 있는 다른 재료의 이름을 담은 문자열이 들어 있어요.
jq로 쇼핑 목록을 어떻게 살펴볼 수 있는지 알아봐요.
쇼핑 목록의 "name" 요소를 출력하는 표현식을 작성해요.
"required" 재료만 세요. 선택적 재료는 세지 않아요.
설탕의 양을 출력하는 표현식을 작성해요. 숫자 부분만 필요해요.
일부 재료는 대체할 수 있어요 (재료 X가 없으면 Y를 사용할 수 있어요). 추천 재료를 그 대체 재료에 매핑한 JSON 객체를 출력해요. 매핑에 선택적 재료도 포함해요.