jq는 들어오는 JSON 데이터를 단일 표현식(_필터들의 파이프라인_으로 작성)에 통과시켜 원하는 변환 결과를 얻는 방식으로 동작해요.
jq 명령줄 옵션 간단히 살펴보기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는 기본적으로 보기 좋게 출력하므로, JSON을 . 필터에 전달하면 보기 좋게 포맷된 출력을 공짜로 얻을 수 있어요!
$ 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 '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
큰 객체에서 키의 일부만 추출하고 싶은 경우가 꽤 흔해요.
예를 들어, 다음에서 id와 name을 추출하려면
{
"id": 101,
"name": "alpha widget",
"specifications": {...}
}
이렇게 작성할 수 있어요
{id: .id, name: .name}
하지만 이건 너무 흔한 패턴이라 축약 문법이 있어요:
{id, name}
예를 들어, 다음 내용이 담긴 file.json이 있다고 해요
{
"key1": "value1",
"key2": [5, 15, 25]
}
key2 _배열_의 길이를 계산해볼까요:
$ jq '.key2 | length' file.json
3
.key2 표현식의 출력을 length의 입력으로 _파이프_하고 있어요. 예상대로 이 함수는 배열의 원소 개수를 출력해요.
이 부분은 jq에 익숙해지는 데 시간이 좀 걸리는 측면이에요.
대부분(전부는 아니지만)의 함수는 데이터를 인자가 아니라 필터의 입력으로 전달하는 _필터_처럼 동작해요.
이 예제에서는 입력 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
]
매뉴얼에서
jq는 JSON과 동일한 데이터 타입 집합을 지원해요. 숫자, 문자열, 불리언, 배열, 객체(JSON 용어로는 문자열 키만 가지는 해시), 그리고 "null"이에요.
이것들에 대해서는 이후 연습 문제에서 더 배우게 될 거예요.
jq에서는 공백이 중요하지 않아요.
코드를 포맷할 때 원하는 대로 공백, 탭, 줄바꿈을 사용해요.
아직 알려진 jq 스타일 가이드는 없어요.
jq의 값은 _불변_이에요.
값을 수정하는 필터는 새로운 값을 출력해요.
이는 jq에 전역 변수가 없다는 뜻이에요.
상태를 한 필터에서 다른 필터로 전달하는 방식에 익숙해져야 해요.
false와 null 값은 거짓으로 간주해요. 그 밖의 모든 값
(숫자 0과 빈 문자열/배열/객체 포함)은 참이에요.
깊이 들어가지는 않고(함수는 다른 연습 문제에서 다룰 주제예요), 여기 몇 가지 유용한 내장 함수를 소개할게요:
배열을 입력으로 받으면, 배열의 원소 개수를 출력해요.
$ 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을 출력해요.
배열을 입력으로, 필터를 인자로 받으면, 각 원소에 필터를 적용한 배열을 출력해요
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
어떤 입력과 필터를 인자로 받으면:
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) ]
주석은 # 문자로 시작하고 줄 끝까지 이어져요.