jqは、コマンドラインでJSONを整形・抽出・加工するためのツールです。curlでAPIから取得したJSONは、そのままだと1行にぎゅっと詰まっていて読みづらいものですが、jqを通すだけで見やすく整形され、さらに「欲しいフィールドだけ抜き出す」「条件で絞り込む」「集計する」といった処理が一発でできます。curl ... | jqの組み合わせは、API操作の定番中の定番です。
この記事では、実機のLinux(WSLのDebian)でJSONを処理しながら、jqの基本から実務パターンまでを整理します。最後には、実際にcurlで取得したAPIのJSONをjqで処理する例も紹介します。jqはWindowsやmacOSでも簡単に導入でき(winget install jqlang.jqやbrew install jq)、一度覚えると手放せなくなります。
- 整形は
jq .、フィールド抽出はjq '.name'、ネストはjq '.a.b'です。 - 配列は
.items[0](要素)、.items[](全要素を展開)で取り出します。 - 文字列をそのまま出す(クォートを外す)には
-rを使います。 - 絞り込みは
select(条件)、変換はmap(...)です。 - 集計は
add(合計)・length(個数)・keys(キー一覧)。 curl ... | jqで、APIのJSONをそのまま処理できます。
JSONを取得するcurlコマンド、テキストの絞り込みgrep、パイプの仕組みリダイレクトとパイプもあわせて参考になります。
整形とフィールド抽出(. / .name)
もっとも基本的な使い方が整形(pretty print)です。jq .にJSONを渡すと、インデント付きで読みやすく表示されます。特定のフィールドを抜き出すには.フィールド名を指定します。
# 整形(1行のJSONを見やすく)
echo '{"a":1,"b":[2,3]}' | jq .
# フィールドを抽出(. のあとにキー名)
jq '.name' data.json # "taro"
# ネストしたフィールドは . でつなぐ
jq '.address.city' data.json # "Tokyo"
# ファイルからでもパイプからでもよい
cat data.json | jq '.name'
実機でも、1行のJSON({"a":1,"b":[2,3]})をjq .に通すとインデント付きで整形表示され、jq '.name'で"taro"、jq '.address.city'で"Tokyo"と、ネストしたフィールドまで.でつないで抽出できることを確認しました。jqへの入力は、ファイルを引数に渡しても、パイプで流し込んでもかまいません。この「.フィールドで掘っていく」という書き方がjqの基本文法です。
配列の扱い(.items[] / [0])
JSONの配列は、[0]で特定の要素、[]で全要素を展開して取り出します。.items[].priceのように書けば、配列内の各オブジェクトから特定のフィールドをまとめて取り出せます。
# 配列の先頭要素 jq '.items[0]' data.json # 配列の特定フィールドを1つ jq '.items[0].price' data.json # 100 # 配列の全要素からフィールドを展開 jq '.items[].price' data.json # 100 # 250 # 80 # 配列そのものの長さ jq '.items | length' data.json # 3
実機でも、.items[0].priceで先頭要素の値(100)、.items[].priceで全要素の価格が一覧展開(100・250・80)されることを確認しました。[0]は配列のインデックスで特定の1つ、[]は「配列を開いて中身を1つずつ流す」イテレーターだとイメージすると分かりやすいです。.items | lengthのように|(パイプ)でつなげば、要素数も取れます(3)。
【重要】-rで生の文字列を出す
jqはデフォルトで文字列をダブルクォート付きで出力します("taro")。シェルスクリプトで値をそのまま使いたいときは、-r(raw output)でクォートを外します。これを知らないと、変数に入れた値にクォートが混ざってハマります。
# デフォルト: クォート付き jq '.name' data.json # "taro" # -r: 生の文字列(クォートなし) jq -r '.name' data.json # taro # シェル変数に入れるときは -r が必須 name=$(jq -r '.name' data.json) echo "こんにちは、$name さん" # こんにちは、taro さん # 一覧をそのままループに使える jq -r '.items[].id' data.json | while read id; do echo "処理中: $id" done
実機で確認したところ、jq '.name'は"taro"(ダブルクォート付き)、jq -r '.name'はtaro(クォートなしの生文字列)を出力しました。シェル変数に代入したり、ファイル名やURLとして使ったりするときは、クォートが邪魔になるので-rが必須です。逆に、そのJSONをまた別のプログラムにJSONとして渡すなら、クォート付き(-rなし)のままにします。「jqの結果を変数に入れたら値に"が付いていた」というのは、-rの付け忘れがほとんどです。パイプでループに渡すときも-rを付けておくと、余計なクォートに悩まされません。
絞り込みと変換(select / map)
jqの真価は絞り込みと変換にあります。select(条件)で条件に合う要素だけを残し、map(...)で配列の各要素を変換できます。SQLのWHEREやSELECTに近い感覚です。
# active が true の要素だけを残し、id を取り出す
jq '[.items[] | select(.active) | .id]' data.json
# [1,3]
# 価格が90より大きい要素を、読みやすい文字列に整形
jq -r '.items[] | select(.price > 90) | "\(.id): \(.price)円"' data.json
# 1: 100円
# 2: 250円
# map で各要素を {id, price} だけの形に変換
jq '.items | map({id, price})' data.json
# [{"id":1,"price":100}, ...]
実機でも、select(.active)でactiveがtrueの要素だけに絞り込み、そのidを配列化して[1,3]を得られました。select(.price > 90)のように数値の比較もでき、さらに"\(.id): \(.price)円"という文字列補間で「1: 100円」のように読みやすく整形できました(\(...)の中にフィールドを埋め込めます)。map({id, price})を使えば、各要素から必要なキーだけを抜き出した新しい配列に変換できます。select(絞り込み)とmap(変換)を組み合わせれば、APIのレスポンスから欲しい形のデータを自在に取り出せます。
集計とキー操作(add / length / keys)
数値の合計や要素の個数、オブジェクトのキー一覧も取れます。ログやAPIレスポンスの簡単な集計に便利です。
# 価格の合計(配列にして add) jq '[.items[].price] | add' data.json # 430 # 要素の個数 jq '.items | length' data.json # 3 # オブジェクトのキー一覧 jq 'keys' data.json # ["address","age","items","name","tags"] # 存在しないキーは null が返る jq '.nokey' data.json # null
実機でも、[.items[].price] | addで価格の合計(430)、lengthで要素数(3)、keysでトップレベルのキー一覧が取得できました。addは「配列の全要素を足す」関数なので、まず[...]で配列にまとめてから渡すのがポイントです。存在しないキーを指定するとnullが返ります(エラーで止まらないので、オプション的なフィールドも安全に扱えます)。jq -eを使うと、結果がnullやfalseのとき終了コードで判定でき、スクリプトでの分岐に使えます(実機でも-eで存在しないキーの終了コードが1になることを確認しました)。
curlと連携する(API処理の定番)
jqが最も輝くのが、curlと組み合わせてAPIのJSONをそのまま処理する場面です。curl -s URL | jq ...という形で、取得と加工を一気に行えます。
# APIのJSONを整形して表示 curl -s https://api.example.com/posts | jq . # 記事タイトルの一覧だけを取り出す curl -s https://api.example.com/posts | jq -r '.[].title' # id と日付を整形して並べる curl -s https://api.example.com/posts | jq -r '.[] | "\(.id): \(.date)"' # 取得件数を数える curl -s https://api.example.com/posts | jq 'length'
この記事の検証では、実際に稼働しているWebサイトのAPI(WordPressのREST API)にcurlでアクセスし、返ってきたJSONをjqで処理しました。curl -s '.../posts' | jq 'length'で取得件数(5件)、jq -r '.[].title.rendered'で記事タイトルの一覧、jq -r '.[] | "\(.id): \(.date)"'でIDと日付を整形した一覧、jq -c '[.[].id]'でIDだけの配列([9340,9335,...])を、すべて正しく取り出せました。生のJSONは1行に詰まって読めませんが、jqを通すだけで必要な情報だけをきれいに抽出できます。API開発・調査でのcurl | jqは、一度使うと手放せない組み合わせです。-cを付けると1行のコンパクトなJSONで出力され、別のコマンドへ渡すのに便利です。
主なフィルタ一覧
jqでよく使うフィルタをまとめます。
| フィルタ | 働き |
|---|---|
. / .key / .a.b |
整形 / フィールド抽出 / ネスト |
.arr[] / .arr[0] |
配列の全要素 / 特定の要素 |
-r / -c |
生文字列 / コンパクト出力 |
select(条件) |
条件で絞り込み |
map(...) |
各要素を変換 |
add / length / keys |
合計 / 個数 / キー一覧 |
よくある失敗
-rを付けず値にクォートが付く
変数やファイル名に使うなら-rで生文字列にします。JSONとして渡すなら付けません。
フィルタをクォートで囲まない
jq '.items[]'のようにシングルクォートで囲みます。囲まないと[]等をシェルが解釈します。
addを配列にせず使う
addは配列に対する関数です。[.items[].price] | addのように配列化してから渡します。
存在しないキーでエラーだと思い込む
存在しないキーはnullが返るだけです。判定したいならjq -eを使います。
curlの出力に進捗が混ざる
curlは-sを付けて進捗を消してからjqへ渡します。
よくある質問
jq '.キー名'で抽出します。ネストはjq '.a.b'、配列はjq '.arr[0]'(特定要素)やjq '.arr[]'(全要素)です。値をシェル変数やファイル名に使うときは、クォートを外す-rを付けてください。-r(raw output)を付けます。jq -r '.name'とすると"taro"ではなくtaroが出力されます。シェル変数への代入や、ループでの利用、ファイル名・URLとして使うときは-rが必須です。逆に、結果を再びJSONとして扱う場合はクォート付きのままにします。curl -s URL | jq '.'のようにパイプでつなぎます。curlには進捗を消す-sを付けるのがポイントです。実機でも、実際のAPI(WordPress REST API)のレスポンスをjq -r '.[].title'でタイトル一覧に、jq 'length'で件数に、と正しく処理できることを確認しています。select(条件)を使います。jq '.items[] | select(.active)'でactiveがtrueの要素だけ、select(.price > 90)で価格が90より大きい要素だけを残せます。結果を配列にまとめたいときは全体を[ ... ]で囲みます(例: [.items[] | select(.active) | .id])。length(例: .items | length)、合計はaddを使います。addは配列に対する関数なので、[.items[].price] | addのように、まず値を配列にまとめてから渡してください。実機でも、合計430・件数3が正しく取得できることを確認しています。まとめ
- 整形は
jq .、抽出は.key・.a.b、配列は.arr[]・.arr[0]。 - 値をそのまま使うなら
-rでクォートを外します。 - 絞り込みは
select(条件)、変換はmap(...)。 - 集計は
add・length・keys。存在しないキーはnull。 curl -s URL | jq ...で、APIのJSONを一気に処理できます。
jqは、JSONを扱うすべての場面で役立つ強力なツールです。「掘るのは.key」「クォートを外すのは-r」「絞るのはselect」——この基本を押さえれば、curlと組み合わせてAPIのレスポンスを自在に加工できるようになります。まずはcurl -s URL | jq .で整形して眺めるところから始めてみましょう。
