Skip to content
ブログに戻る
チュートリアル

jq チートシート:実戦で使える 30 個の JSON コマンドライン・パターン

jq を使いこなす 30 の実戦パターン:kubectl、AWS CLI、ログ解析まで、コマンドラインでの JSON 処理を完全網羅。

12 分で読める

jq チートシート:実戦で使える 30 個の JSON コマンドライン・パターン

kubectl get pods -o jsonless にパイプしたら、2 メガバイトの JSON に画面が固まる。欲しいのは Running 状態のすべての pod 名だけ。jq ならフィルタ構文 3 文字でそれができる——語彙さえ分かれば。

本稿は構文リファレンスではない。あなたが実際に打ち込む 30 個のパターンを、目的別にまとめたものだ:アクセス、フィルタ、変換、集計、整形、そして kubectlawsdocker といった実ツールとの組み合わせ。

jq・ブラウザ formatter・コードの使い分け

jq が常に正解とは限らない。正直に選ぶなら次の 3 通り:

場面最適ツール理由
単一 API レスポンス、シンタックスハイライトとエラー行が欲しいブラウザの JSON Formatterビジュアル差分、設定ゼロ、ブラウザ内で完結
シェルパイプライン、ログ処理、CI スクリプト、リモートサーバjq組み合わせ可能、スクリプト化可能、GUI 依存なし
業務ロジック、単体テスト、複雑な分岐プログラム(JS / Python)本物のデバッガ、型、ライブラリ

タスクがシェルパイプラインの中にあるときは jq。それ以外は別の場所のほうが楽なことが多い。

インストールと最初のパイプライン

jq は主要プラットフォームすべてに単一バイナリで提供される:

# macOS
brew install jq

# Debian / Ubuntu
sudo apt install jq

# Windows(winget)
winget install jqlang.jq

identity filter を使った最初のパイプライン:

curl -s https://api.github.com/users/octocat | jq .

. フィルタは入力をそのまま整形して出力する。これだけで「JSON をエディタで開こう」という瞬間のほとんどが不要になる。

実戦の 90 % をカバーする 5 つのフラグ:

フラグ役割
-rraw output——文字列結果の外側の引用符を外す
-ccompact——1 行 1 JSON 値(NDJSON)
-sslurp——すべての入力を 1 つの配列にまとめる
-Rraw input——行を JSON ではなく文字列として読む
-nnull input——stdin を読まず、null を入力として使う

コアメンタルモデル:Filter と Pipe

Filter は 1 つの JSON 値を入力に受け取り、0 個以上の JSON 値を出力する。Filter はパイプ | で合成し、左側のフィルタの各出力を右側のフィルタの入力に流す。モデルはシェルパイプと同じで、バイトではなく JSON 値が流れるだけだ。

# . —— identity
echo '{"name":"Alice"}' | jq '.'

# .key —— フィールドアクセス
echo '{"name":"Alice"}' | jq '.name'

# .key.sub —— 深いパス
echo '{"user":{"email":"a@x.com"}}' | jq '.user.email'

# .[] —— 配列要素をイテレート(複数出力)
echo '[{"id":1},{"id":2}]' | jq '.[] | .id'

# パイプ合成:.items[] の各出力が .name に流れる
echo '{"items":[{"name":"a"},{"name":"b"}]}' | jq '.items[] | .name'

文法はこれだけ。以下の 30 パターンはすべてこれらプリミティブの組み合わせだ。

実際に使う 30 パターン

各パターンに入力 JSON、コマンド、出力を示す。そのままターミナルにコピーして動く。

アクセスと抽出(パターン 1–5)

パターン 1 —— ? による安全なアクセス

存在しない可能性のあるフィールドに、クラッシュせずアクセス:

echo '{"name":"Alice"}' | jq '.address?.city?'
# 出力: null

? は欠損キーのエラーを抑制する。なしだと .address がなければ .address.city は型エラーを投げる。

パターン 2 —— 深いパスアクセス

echo '{"user":{"profile":{"email":"a@x.com"}}}' | jq '.user.profile.email'
# 出力: "a@x.com"

パターン 3 —— 配列スライス

echo '[10,20,30,40,50]' | jq '.[1:3]'
# 出力: [20, 30]

echo '[10,20,30,40,50]' | jq '.[-1]'
# 出力: 50

負のインデックスは末尾から数える。スライスは Python と同じく半開区間。

パターン 4 —— 再帰下降で全マッチキーを抽出

echo '{"a":{"name":"x"},"b":[{"name":"y"},{"id":1}]}' | jq '.. | .name? | select(. != null)'
# 出力: "x"
#       "y"

.. はツリー内の全値を走査する。.name?select と組み合わせると、深さを問わずすべての name フィールドを抽出できる——未知の JSON スキーマを探索するのに不可欠。

パターン 5 —— オブジェクトの全キーをリスト

echo '{"zebra":1,"apple":2,"mango":3}' | jq 'keys'
# 出力: ["apple", "mango", "zebra"]

echo '{"zebra":1,"apple":2,"mango":3}' | jq 'keys_unsorted'
# 出力: ["zebra", "apple", "mango"]

keys はアルファベット順、keys_unsorted は挿入順を保持。

フィルタ(パターン 6–10)

パターン 6 —— 条件で配列をフィルタ

echo '[{"age":20},{"age":30},{"age":40}]' | jq 'map(select(.age > 25))'
# 出力: [{"age":30},{"age":40}]

map(f) は各要素に f を適用。select(cond) は条件が真の要素のみ残す。

パターン 7 —— 文字列の前方一致

echo '[{"name":"api-gateway"},{"name":"web-ui"},{"name":"api-auth"}]' \
  | jq '.[] | select(.name | startswith("api"))'
# 出力: {"name":"api-gateway"}
#       {"name":"api-auth"}

他にも endswith("...")contains("...")test("^regex$") が使える。

パターン 8 —— 複合条件

echo '[{"type":"A","count":5},{"type":"A","count":15},{"type":"B","count":20}]' \
  | jq '.[] | select(.type == "A" and .count > 10)'
# 出力: {"type":"A","count":15}

andornot は期待どおりに動く。

パターン 9 —— 機密フィールドを削除

echo '{"user":"alice","password":"s3cret","token":"abc"}' | jq 'del(.password, .token)'
# 出力: {"user":"alice"}

del() は複数パスを受け取り、どれか欠けていてもエラーにならない。

パターン 10 —— フィールドで重複排除

echo '[{"id":1,"v":"a"},{"id":2,"v":"b"},{"id":1,"v":"a2"}]' | jq 'unique_by(.id)'
# 出力: [{"id":1,"v":"a"},{"id":2,"v":"b"}]

unique は値全体で重複排除、unique_by(f) はフィルタ結果で重複排除する。

変換(パターン 11–15)

パターン 11 —— フィールドのリネーム

echo '[{"first_name":"Alice","age":30}]' | jq 'map({name: .first_name, age})'
# 出力: [{"name":"Alice","age":30}]

{age}{age: .age} の省略形。

パターン 12 —— 文字列補間で計算フィールドを追加

echo '[{"first":"Alice","last":"Chen"}]' \
  | jq 'map(. + {fullName: "\(.first) \(.last)"})'
# 出力: [{"first":"Alice","last":"Chen","fullName":"Alice Chen"}]

\(expr)expr を評価し、その値を文字列に埋め込む。

パターン 13 —— ネスト配列をフラット化

echo '[{"tags":["a","b"]},{"tags":["c"]}]' | jq '[.[] | .tags[]]'
# 出力: ["a","b","c"]

echo '[[1,2],[3,[4,5]]]' | jq 'flatten'
# 出力: [1,2,3,4,5]

flatten は深さ引数を取る:flatten(1) は 1 段階だけ剥がす。

パターン 14 —— オブジェクトと配列の相互変換

echo '{"a":1,"b":2}' | jq 'to_entries'
# 出力: [{"key":"a","value":1},{"key":"b","value":2}]

echo '[{"key":"a","value":1},{"key":"b","value":2}]' | jq 'from_entries'
# 出力: {"a":1,"b":2}

この組み合わせで、ドットパス構文では直接できない「オブジェクトのキーを走査する変換」が可能になる。

パターン 15 —— 2 つのオブジェクトの深いマージ

echo '{"a":{"x":1},"b":2}' | jq '. * {a:{y:9}, c:3}'
# 出力: {"a":{"x":1,"y":9},"b":2,"c":3}

* 演算子は深いマージ。浅いマージには + を使う(右側が勝つ)。

集計(パターン 16–20)

パターン 16 —— 配列・オブジェクト・文字列の長さ

echo '[1,2,3,4]' | jq 'length'      # 4
echo '{"a":1,"b":2}' | jq 'length'  # 2
echo '"hello"' | jq 'length'        # 5

パターン 17 —— フィールドの合計

echo '[{"price":10},{"price":25},{"price":5}]' | jq '[.[].price] | add'
# 出力: 40

add は入力型に応じて、数値の合計、文字列の連結、配列のマージをこなす。

パターン 18 —— フィールドでグルーピング

echo '[{"cat":"A","n":1},{"cat":"B","n":2},{"cat":"A","n":3}]' | jq 'group_by(.cat)'
# 出力: [[{"cat":"A","n":1},{"cat":"A","n":3}],[{"cat":"B","n":2}]]

各グループが内部配列になる。map と組み合わせてグループごとに集計。

パターン 19 —— 降順ソート

echo '[{"date":"2026-01-03"},{"date":"2026-01-01"},{"date":"2026-01-02"}]' \
  | jq 'sort_by(.date) | reverse'
# 出力: [{"date":"2026-01-03"},{"date":"2026-01-02"},{"date":"2026-01-01"}]

ISO 8601 日付文字列は文字列として正しくソートされる。他フォーマットは先にパースが必要——Unix タイムスタンプ完全ガイド が epoch 秒、ミリ秒、タイムゾーン変換を詳しくカバーしている。

パターン 20 —— フィールドで最大・最小

echo '[{"name":"a","rating":4.1},{"name":"b","rating":4.8},{"name":"c","rating":3.9}]' \
  | jq 'max_by(.rating)'
# 出力: {"name":"b","rating":4.8}

min_bymax_by は単一要素を返す。上位 N 件は sort_by(.rating) | reverse | .[:N]

出力整形(パターン 21–25)

パターン 21 —— CSV 出力

echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' \
  | jq -r '.[] | [.name, .age] | @csv'
# 出力: "Alice",30
#       "Bob",25

@csv は文字列を引用符で囲み、内部の引用符をエスケープする。-r で JSON 文字列の外側引用符を外すと、CSV として直接パイプできる。パイプライン内での CSV と JSON の完全な相互変換は CSV と JSON 相互変換ガイド を参照。

パターン 22 —— TSV 出力

echo '[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]' \
  | jq -r '.[] | [.id, .name] | @tsv'
# 出力: 1	Alice
#       2	Bob

タブ区切り出力は cutawkcolumn -t と相性がよい。

パターン 23 —— 生の文字列出力

echo '["alpha","beta"]' | jq -r '.[]'
# 出力: alpha
#       beta

-r なしだと各行に引用符がつく。xargswhile read、別シェルコマンドに食わせるのは raw output。

パターン 24 —— NDJSON / JSON Lines

echo '[{"a":1},{"a":2}]' | jq -c '.[]'
# 出力: {"a":1}
#       {"a":2}

各行が独立した JSON 値——Kafka、Elasticsearch、多くの構造化ロガーが使うフォーマット。-c は内部の空白もすべて除く。

パターン 25 —— 文字列補間による整形出力

echo '[{"name":"server-1","cpu":0.73},{"name":"server-2","cpu":0.21}]' \
  | jq -r '.[] | "\(.name): \(.cpu * 100)% CPU"'
# 出力: server-1: 73% CPU
#       server-2: 21% CPU

サマリやログ行に最適。生 JSON ではノイズになる場面で効く。

DevOps 現場(パターン 26–30)

パターン 26 —— kubectl:実行中の全 pod 名

kubectl get pods -o json \
  | jq -r '.items[] | select(.status.phase=="Running") | .metadata.name'

pod をイテレート、Running のみ残し、名前を raw 文字列として出力。

パターン 27 —— AWS EC2:インスタンス ID と公開 IP

aws ec2 describe-instances \
  | jq -r '.Reservations[].Instances[] | [.InstanceId, .PublicIpAddress // "none"] | @tsv'

// alternative operator はフィールドが null のときデフォルト値を提供する——出力カラムに null リテラルを出さずに済む。

パターン 28 —— GitHub API:ページ結果のマージ

for p in 1 2 3; do
  curl -s "https://api.github.com/orgs/myorg/repos?per_page=100&page=$p"
done | jq -s 'add | map(.name)'

-s で全レスポンスを配列の配列にまとめ、add で連結、map(.name) で名前を抽出。ページング API 全般に使える定番。

パターン 29 —— 構造化ログファイルをフィルタ

cat app.log | jq -c 'select(.level=="error")'

ログファイルが NDJSON(1 行 1 JSON オブジェクト)であることが前提。tail -f と組み合わせてリアルタイム監視:

tail -f app.log | jq -c 'select(.level=="error") | {ts: .timestamp, msg: .message}'

パターン 30 —— Docker:使用中の全イメージ名

docker inspect $(docker ps -q) | jq -r '.[].Config.Image' | sort -u

1 台のホストでどのイメージバージョンが動いているかをサッと確認できる。

よくあるエラーと修正法

jq ユーザなら誰もが踏む罠。対処を先に知っておくと時間が浮く。

Cannot iterate over null (null)

イテレートしようとしたフィールドが null または不在。修正は 2 通り:

# 方法 A:optional operator
echo '{}' | jq '.items[]?'
# 出力: (なし、エラーも出ない)

# 方法 B:alternative operator とデフォルト
echo '{}' | jq '(.items // [])[]'
# 出力: (なし、エラーも出ない)

静かにスキップしたいなら ?。後続フィルタに具体的な空配列を渡したいなら // []

Cannot index array with "key"

.foo と書いたが現在の値が配列だった。[] でイテレートを追加:

# 間違い
echo '{"users":[{"name":"Alice"}]}' | jq '.users.name'
# エラー: Cannot index array with "name"

# 正しい
echo '{"users":[{"name":"Alice"}]}' | jq '.users[].name'
# 出力: "Alice"

シェルのクォート問題

jq プログラム全体をシングルクォートで囲み、内部の文字列リテラルにダブルクォートを使う:

# どこでも動く
jq '.users[] | select(.role == "admin")'

# 壊れる —— ダブルクォートを先にシェルが解釈する
jq ".users[] | select(.role == \"admin\")"

Windows PowerShell のエッジケース

PowerShell はシングルクォートを bash とは違って扱う。プログラム全体をダブルクォートで囲んで内部引用符をエスケープするか、here-string を使う:

jq "@'
.users[] | select(.role == \"admin\")
'@"

ちょっとでも複雑なら、フィルタを .jq ファイルに保存して jq -f filter.jq で実行する。

-r の誤用

-r は文字列結果にのみ効く。オブジェクトに対して使っても通常の JSON オブジェクトが出る:

echo '{"a":1}' | jq -r '.'
# 出力: {"a":1}     ← そのまま。-r が剥がすものがなかった

特定フィールドを引用符なしで出したいなら、先にそれを select:jq -r '.a'

jq はコメントや末尾カンマを含む JSON を拒否する

echo '{"a": 1, /* メモ */ "b": 2,}' | jq .
# parse error: Invalid numeric literal

jq は RFC 8259 準拠の厳密な JSON のみを扱う——コメント、末尾カンマ、クォートなしキーは不可。ファイルが JSON5 または JSONC(設定ファイルでよくある)の場合、先に拡張部分を剥がしてから jq にパイプする。JSON5 と JSONC フォーマットガイド に、対応するパーサと厳密 JSON への変換方法をまとめている。

jq vs 代替:gron・fx・jj・yq

jq 一択ではなく、別ツールの方が速い場面もある:

ツール強み採用すべき場面
gronJSON を grep 可能なパス文字列に平坦化未知スキーマの探索——キーがどこにあるか分からないとき
fxハイライト付きの対話 TUI エクスプローラ大きな JSON ファイルを手作業で眺める
jjjq より圧倒的に速い、構文は限定的何百万レコードを処理するホットループ
yq同じフィルタ言語で YAML を扱うKubernetes マニフェストや CI 設定
ブラウザ JSON Formatterシンタックスハイライト、精密なエラー、インストール不要開発中の単一レスポンスのデバッグ

日常のシェル作業では合成性で jq が勝つ。一度きりの探索なら gron が速いことが多い。YAML は yq を使う——yq の後に jq をパイプする構成は避ける。

日常のプロ Tips

jq が手に馴染むようになる習慣をいくつか:

  1. $HOME.jqrc を置く。よく使う関数を書いておくと、すべての jq 呼び出しで即使える:

    def running: select(.status.phase == "Running");
    def table(f): [f] | @tsv;
  2. 複雑なフィルタは jqplay.org で試す。左に JSON を貼り、右でフィルタをイテレートし、動いたものをスクリプトに入れる。

  3. history から自分のチートシートを作るhistory | grep 'jq ' | sort -u > ~/jq-patterns.txt で実際に使ったパターンを全て拾える。

  4. 未知のスキーマはブラウザ JSON Formatter と組み合わせる。まずビジュアルに構造を探索してパスを特定し、それから jq コマンドを書く。

  5. ライブ値の監視watch -n 5 "curl -s api.example.com/health | jq '.uptime'" が 5 秒ごとに更新——依存ゼロのミニ ops ダッシュボードになる。

FAQ

jq とは何で、なぜ開発者が使うのか?

jq はコマンドライン JSON プロセッサ。Python や Node のスクリプトなしで、シェルパイプライン内で JSON を抽出・フィルタ・変換できる——API レスポンス、ログファイル、kubectl 出力から目的のフィールドへの最短路だ。

Windows で jq は使える?

使える。winget install jqlang.jq、Chocolatey の choco install jq、または jqlang.org からバイナリをダウンロード。PowerShell のクォートルールは bash と違うので、迷ったらフィルタを .jq ファイルに保存して jq -f filter.jq で実行するのがよい。

jq とブラウザの JSON formatter の違いは?

ブラウザの JSON Formatter は対話式——JSON を貼り、ハイライトとエラーを見て、結果をコピー。jq は非対話式——フィルタで変換を一度記述し、シェルパイプラインで走らせる。単一レスポンスのデバッグにはブラウザ、何百もの入力に同じ処理を自動化するには jq

なぜ jq が「Cannot iterate over null」と言うのか?

.[]null 値をイテレートしようとした——入力にそのフィールドがなかったためだ。optional operator .items[]? を使うか、.items // [] | .[] でデフォルト値を与える。

jq でファイルをインプレース編集できる?

直接はできない——jq は stdout に書く。一時ファイルか moreutils の sponge を使う:jq '.version = "2.0"' config.json | sponge config.json。事前に必ず元ファイルをバックアップしておくこと——フィルタを書き間違えるとファイルが上書きされる。

curl のレスポンスに jq を使うには?

curl -sjq にパイプする。-s フラグで curl の進捗バーを抑え、JSON 本体だけが jq に流れる:

curl -s https://api.github.com/users/octocat | jq '.name, .blog'

jq の | とシェルの | の違いは?

シェルパイプはプロセス間でバイトを渡す。jq のパイプは同一 jq 呼び出し内のフィルタ間で JSON 値を渡す。内部パイプを多数含むコマンドもプロセス 1 個で動く——jq | jq | jq のチェーンより軽い。

jq は JSON Lines(NDJSON)を扱える?

ネイティブにできる。入力が空白で区切られていれば、jq は各行を独立した JSON 値として読む。-c で NDJSON を出力し、-s で NDJSON を単一配列に集める。

フィルタなしで JSON を整形するには?

identity filter を使う:cat data.json | jq . または jq . < data.json。パース・検証・2 スペース字下げで整形——フィルタは不要。

GUI 付きの jq 代替はある?

ある。fx が対話 TUI を提供する。インストール不要の GUI なら、ブラウザベースの JSON Formatter が「探索して検証」という多くのニーズをカバーする。Web ツールの jqplay.org は jq そのものをライブプレビューで使える。

jq と Python スクリプト、どちらを選ぶべき?

タスクが一度きりで、シェルパイプラインに収まり、filter/transform/extract の意味論で足りるなら jq。ユニットテスト、複雑な状態、サードパーティライブラリ、.jq ファイルの可読性を超える分岐ロジックが必要なら Python に切り替える。

jq で正規表現はどう使う?

jqtest("pattern")match("pattern")capture("pattern")scan("pattern") で正規表現を提供し、すべて PCRE 構文。第 2 引数でフラグを指定:test("abc"; "i") で大文字小文字を無視。match はオフセットとキャプチャを返し、scan は非重複の全マッチを列挙する。

jq の出力で日本語が \uXXXX にエスケープされる。元の文字を残すには?

jq はデフォルトで非 ASCII 文字を UTF-8 のまま出力する——ただし -a / --ascii-output を付けていなければ。\u65e5\u672c\u8a9e のようなエスケープが出る場合、通常は上流ツールか端末 locale が原因。$LANGja_JP.UTF-8 または en_US.UTF-8 になっていることを確認し、-a flag を避けること。

要点

  1. メンタルモデルが先:フィルタが 1 入力を受け取り、0 個以上の JSON 値を出す。合成は |。残りは構文の詳細。
  2. 演算子ではなくタスクで学ぶ:上記 30 パターンで日常の jq 使用の約 95 % をカバーする。
  3. null は明示的に扱う:静かにスキップなら ?、具体的なフォールバックなら // defaultCannot iterate over null の修正はほぼこの 2 択。
  4. jq が不向きな場面を知る:単一レスポンスはブラウザの JSON Formatter に任せる、YAML は yq、複雑なロジックは本物のコード。
  5. 既存スタックと組み合わせるjqcurlkubectlawsdocker、ログパイプラインの中で輝く。接着剤として使い、ロジック層にはしない。

関連 JSON ワークフローの参考:設定ファイルの構文拡張は JSON5 と JSONC フォーマットガイド、データフォーマット移行における jq の位置づけは CSV と JSON 相互変換ガイド。JSON にタイムスタンプが含まれるなら、Unix タイムスタンプ完全ガイド が日付フィールド変換時の落とし穴をまとめている。