jq — JSON クエリと変換
選択的読み取りルール
以下から開始:
references/senior-master-standard.mdreferences/usage-routing.mdreferences/quality-checklist.md
次に、ユーザーの実際のタスクに一致する継承されたドキュメント、スクリプト、アセット、または例のみを読み込みます。
概要
jq は JSON をクエリし再整形するための標準 CLI ツールです。このスキルは実用的でエキスパートレベルの使用法をカバーします: 深くネストされたデータのフィルタリング、構造の変換、値の集約、および jq をシェルパイプラインに組み込むこと。すべての例は実際のワークフローにコピー&ペースト可能です。
このスキルを使用すべき時
- API、CLI ツール(AWS、GitHub、kubectl、docker)、またはログファイルからの JSON 出力を解析する時
- JSON 構造を変換する時(キー名の変更、配列の平坦化、レコードのグループ化)
- ユーザーが bash スクリプトやワンライナー内で
jqを必要とする時 - 複雑な
jq式が何をするかを説明する時
仕組み
jq はフィルター式を受け取り、それを JSON 入力に適用します。フィルターはパイプ (|) で合成され、jq は配列、オブジェクト、文字列、数値、ブール値、および null をネイティブに処理します。
基本選択
# フィールドを抽出
echo '{"name":"alice","age":30}' | jq '.name'
# "alice"
# ネストされたアクセス
echo '{"user":{"email":"[email protected]"}}' | jq '.user.email'
# 配列インデックス
echo '[10, 20, 30]' | jq '.[1]'
# 20
# 配列スライス
echo '[1,2,3,4,5]' | jq '.[2:4]'
# [3, 4]
# すべての配列要素
echo '[{"id":1},{"id":2}]' | jq '.[]'
select によるフィルタリング
# 一致する要素のみを保持
echo '[{"role":"admin"},{"role":"user"},{"role":"admin"}]' \
| jq '[.[] | select(.role == "admin")]'
# 数値比較
curl -s https://api.github.com/repos/owner/repo/issues \
| jq '[.[] | select(.comments > 5)]'
# フィールドが存在し、null でないことをテスト
jq '[.[] | select(.email != null)]'
# 条件の組み合わせ
jq '[.[] | select(.active == true and .score >= 80)]'
マッピングと変換
# すべての配列要素からフィールドを抽出
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' \
| jq '[.[] | .name]'
# ["alice", "bob"]
# 省略形: map()
jq 'map(.name)'
# 要素ごとに新しいオブジェクトを構築
jq '[.[] | {user: .name, years: .age}]'
# 計算フィールドを追加
jq '[.[] | . + {senior: (.age > 28)}]'
# キー名の変更
jq '[.[] | {username: .name, email_address: .email}]'
集約とリデュース
# すべての値の合計
echo '[1, 2, 3, 4, 5]' | jq 'add'
# 15
# オブジェクト全体でのフィールドの合計
jq '[.[].price] | add'
# 要素数のカウント
jq 'length'
# 最大 / 最小
jq 'max_by(.score)'
jq 'min_by(.created_at)'
# reduce: カスタムアキュムレータ
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
# 15
# フィールドによるグループ化
jq 'group_by(.department)'
# グループごとのカウント
jq 'group_by(.status) | map({status: .[0].status, count: length})'
文字列補間とフォーマット
# 文字列補間
jq -r '.[] | "\(.name) is \(.age) years old"'
# CSV形式(ヘッダーなし)
jq -r '.[] | [.name, .age, .email] | @csv'
# TSV形式
jq -r '.[] | [.name, .score] | @tsv'
# URLエンコード
jq -r '.query | @uri'
# Base64エンコード
jq -r '.data | @base64'
キーとパスの操作
# すべてのトップレベルキーを一覧表示
jq 'keys'
# キーが存在するか確認
jq 'has("email")'
# キーを削除
jq 'del(.password)'
# すべての要素からネストされたキーを削除
jq '[.[] | del(.internal_id, .raw_payload)]'
# 再帰的下降: ツリー内の任意の場所にあるキーのすべての値を見つける
jq '.. | .id? // empty'
# すべてのリーフパスを取得
jq '[paths(scalars)]'
条件分岐とエラーハンドリング
# if-then-else
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
# 代替演算子: null または false の場合にフォールバックを使用
jq '.nickname // .name'
# try-catch: 停止する代わりにエラーをスキップ
jq '[.[] | try .nested.value catch null]'
# // empty で null 出力を抑制
jq '.[] | .optional_field // empty'
実用的なシェル統合
# ファイルから読み取り
jq '.users' data.json
# さらにパイプするためのコンパクト出力(空白なし)
jq -c '.[]' records.json | while IFS= read -r record; do
echo "Processing: $record"
done
# シェル変数を jq に渡す
STATUS="active"
jq --arg s "$STATUS" '[.[] | select(.status == $s)]'
# 数値を渡す
jq --argjson threshold 42 '[.[] | select(.value > $threshold)]'
# 複数の JSON 行を配列に slurp
jq -s '.' records.ndjson
# 複数ファイル: すべてを1つの配列に slurp
jq -s 'add' file1.json file2.json
# コマンドからの null 安全パイプライン
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
# GitHub CLI: PR 番号を抽出
gh pr list --json number,title | jq -r '.[] | "\(.number)\t\(.title)"'
# AWS CLI: 実行中のインスタンス ID を一覧表示
aws ec2 describe-instances \
| jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'
# Docker: コンテナ名とイメージを表示
docker inspect $(docker ps -q) | jq -r '.[] | "\(.Name)\t\(.Config.Image)"'
高度なパターン
# オブジェクトの配列をオブジェクトの配列に転置
# 入力: {"names":["a","b"],"scores":[10,20]}
jq '[.names, .scores] | transpose | map({name: .[0], score: .[1]})'
# 1レベル平坦化
jq 'flatten(1)'
# フィールドによる一意化
jq 'unique_by(.email)'
# 並べ替え、重複排除、再インデックス
jq '[.[] | .name] | unique | sort'
# walk: すべてのノードに変換を再帰的に適用
jq 'walk(if type == "string" then ascii_downcase else . end)'
# env: jq 内で環境変数を読み取り
export API_KEY=secret
jq -n 'env.API_KEY'
ベストプラクティス
jqの結果をシェル変数や他のコマンドに渡すときは、JSON 文字列の引用符を取り除くために常に-r(生出力)を使用する- シェル変数を安全に注入するために
--arg/--argjsonを使用する — フィルター文字列にシェル変数を直接展開しない - 可読性のために
map(f)を[.[] | f]よりも優先する - 改行区切りの JSON パイプラインには
-c(コンパクト)を使用し、人間が読めるデバッグには省略する - スクリプトに埋め込む前に、
jq -nとリテラル入力でフィルターを対話的にテストする nullにフィルタリングする代わりにemptyを使用して不要な要素を削除する
セキュリティと安全性に関する注意
jqは設計上読み取り専用です — ファイルを書き込んだりコマンドを実行したりできません- 信頼できない JSON フィールド値をシェルコマンドに直接埋め込まないでください。常に引用符で囲むか
--argを使用してください
よくある落とし穴
-
問題:
jqが期待する値の代わりにnullを出力する 解決策: キー名のタイプミスを確認する;keysを使用して実際のフィールド名を検査する。JSON は大文字小文字を区別することを忘れないでください。 -
問題: 出力で数値が文字列として引用符で囲まれている 解決策: 数値を注入するときは
--argの代わりに--argjsonを使用する。 -
問題: フィルターはターミナルで機能するが、スクリプトでは失敗する 解決策: フィルター文字列がシェル内でシングルクォートを使用して変数展開を防いでいることを確認する。例:
jq '.field'でありjq ".field"ではない。 -
問題: 空の配列で
addがnullを返す 解決策:add // 0またはadd // ""を使用してフォールバックデフォルトを提供する。 -
問題: 大きなファイルのストリーミングが遅い 解決策: 非常に大きなファイルには
jq --streamを使用するか、jstream/gronに切り替える。
関連スキル
@bash-pro— jq 呼び出しを堅牢なシェルスクリプトでラップする@bash-linux— 一般的なシェルパイプラインパターン@github-automation— GitHub CLI の JSON 出力と jq を使用する
制限事項
- このスキルは、タスクが上記の範囲と明確に一致する場合にのみ使用してください。
- 出力を環境固有の検証、テスト、またはエキスパートレビューの代わりとして扱わないでください。
- 必要な入力、権限、安全境界、または成功基準が欠落している場合は、立ち止まって明確化を求めてください。