jq 完全ガイド — AWS CLI / kubectl 出力を自在に扱う

jq は JSON 用の sed/awk と言える小さな関数型スクリプト言語。AWS CLI の --output json や kubectl の -o json から欲しい情報だけ抽出・整形・集計する用途で日常的に使います。 AWS CLI には --query (JMESPath) も内蔵されてますが、複雑な処理は jq の方が表現力が高いです。

サンプル JSON として下記を仮定します(適宜置き換えて読んでください):

{
  "users": [
    { "id": 1, "name": "alice", "tags": ["admin", "ops"], "score": 92 },
    { "id": 2, "name": "bob",   "tags": ["dev"],         "score": 78 },
    { "id": 3, "name": "carol", "tags": ["dev", "ops"],  "score": 85 }
  ]
}

目次

  1. 基本フィルタ
  2. 配列・オブジェクト操作
  3. select / map — 絞り込みと変換
  4. group_by / sort_by / unique
  5. reduce / 集計関数
  6. to_entries / from_entries
  7. 複数入力・変数・関数定義
  8. AWS / k8s 実戦レシピ
  9. 用語集

1. 基本フィルタ

jq 式意味
.全体(identity)
.usersキー users の値
.users[0]配列の 0 番目
.users[]配列を「ストリーム」として展開
.users | length長さ
.users[0].nameネストアクセス
.users[].name全 user の name(複数値)
.users[].tags[0]各 user の最初のタグ
.users[].name?キーが無くてもエラーにしない
keysオブジェクトのキー一覧
type型名 (string / number / object / array / boolean / null)
cat data.json | jq .                              # 整形表示
cat data.json | jq -r '.users[].name'             # raw 出力(クォート無し)
echo '{"a":1}' | jq -c .                          # compact (1 行)
cat data.json | jq -S .                           # キーをソート
cat data.json | jq '.users | length'              # 件数
cat data.json | jq '.users[0]'                    # 先頭オブジェクト
-r (raw) は文字列出力時にクォートを取り除く。bash の変数代入や file パスとして使う時は必須。 -c (compact) は API レスポンス保存時、-S は git diff しやすくする用。

2. 配列・オブジェクト操作

# 配列スライス
echo '[1,2,3,4,5]' | jq '.[1:3]'                  # [2, 3]
echo '[1,2,3,4,5]' | jq '.[-2:]'                  # [4, 5] (末尾 2 件)

# 結合
echo '{"a":1, "b":2}' | jq '. + {"c":3}'          # マージ
echo '[1,2] [3,4]' | jq -s 'add'                  # スリュル全体を 1 配列に → 結合
echo '{"a":1}{"b":2}' | jq -s 'add'               # オブジェクト結合

# 削除
jq 'del(.users[0])' data.json                     # 0 番目を削除
jq 'del(.users[].score)' data.json                # 全 user の score を削除

# 追加・更新(| =)
jq '.users[].active = true' data.json
jq '.users[0].name = "ALICE"' data.json
jq '.users |= . + [{"id":4,"name":"dave"}]' data.json  # 配列末尾追加

# パス指定
jq 'paths' data.json                              # 全パス列挙
jq 'paths(numbers)' data.json                     # number のあるパスだけ

3. select / map — 絞り込みと変換

# 条件絞り込み
jq '.users[] | select(.score > 80)' data.json
jq '.users[] | select(.tags | contains(["ops"]))' data.json
jq '.users[] | select(.name | startswith("a"))' data.json
jq '.users[] | select(.tags | length == 1)' data.json

# 複数条件
jq '.users[] | select(.score > 80 and (.tags | contains(["ops"])))' data.json

# キーの存在チェック
jq '.users[] | select(has("score"))' data.json

# null チェック
jq '.users[] | select(.score != null)' data.json

# map — 配列を写像
jq '.users | map(.name)' data.json                # 名前配列
jq '.users | map(.score * 1.1)' data.json         # 全スコアを 10% 増し
jq '.users | map({id, name})' data.json           # 必要な属性だけ
jq '.users | map(select(.score > 80))' data.json  # フィルタ + 配列維持

# map_values — オブジェクトの各値を変換
echo '{"a":1,"b":2}' | jq 'map_values(. * 2)'

4. group_by / sort_by / unique

# sort
jq '.users | sort_by(.score)' data.json           # 昇順
jq '.users | sort_by(.score) | reverse' data.json # 降順
jq '.users | sort_by(.tags | length)' data.json   # 計算式でも可

# unique
echo '[1,2,2,3,3,3]' | jq 'unique'
jq '[.users[].tags[]] | unique' data.json         # 全 tag のユニークリスト

# group_by — 同じキーごとに配列をネスト
jq '.users | group_by(.score > 80)' data.json
jq '.users | group_by(.tags[0])' data.json

# group + count
jq '.users | group_by(.tags[0]) | map({tag: .[0].tags[0], count: length})' data.json

# min/max
jq '[.users[].score] | min' data.json
jq '[.users[].score] | max' data.json
jq '[.users[].score] | (add / length)' data.json  # 平均

5. reduce / 集計関数

# reduce — アキュムレータで畳み込み
jq '.users | reduce .[] as $u (0; . + $u.score)' data.json   # 合計
jq '.users | reduce .[] as $u ({}; .[$u.name] = $u.score)' data.json  # name → score の辞書

# 組み込み集計
jq '.users | map(.score) | add' data.json                    # 合計
jq '.users | map(.score) | length' data.json
jq '.users | map(.score) | (add / length)' data.json         # 平均
jq '.users | min_by(.score)' data.json                       # スコア最小の user
jq '.users | max_by(.score)' data.json

# any / all
jq '.users | any(.score > 90)' data.json                     # true if 1 人でも
jq '.users | all(.score > 60)' data.json

# 文字列結合
jq -r '.users | map(.name) | join(", ")' data.json
jq -r '.users[] | "\(.id): \(.name)"' data.json              # 文字列補間

6. to_entries / from_entries

オブジェクトを配列風に扱える強力イディオム。AWS Tag のような [{Key:..., Value:...}] 形式と KV オブジェクトの相互変換に超頻出。

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

# 逆向き
echo '[{"key":"a","value":1}]' | jq 'from_entries'

# 値による絞り込み(オブジェクト → 配列 → filter → オブジェクト)
echo '{"a":1,"b":2,"c":3}' | jq 'to_entries | map(select(.value > 1)) | from_entries'

# AWS Tag → KV オブジェクト
echo '[{"Key":"Env","Value":"prod"},{"Key":"App","Value":"web"}]' \
  | jq 'map({(.Key): .Value}) | add'
# 結果: {"Env":"prod","App":"web"}

# KV オブジェクト → AWS Tag
echo '{"Env":"prod","App":"web"}' \
  | jq 'to_entries | map({Key: .key, Value: .value})'

# キーのリネーム
echo '{"old_name":1}' | jq 'with_entries(.key |= sub("old"; "new"))'

7. 複数入力・変数・関数定義

# --slurp (-s) — 入力を 1 配列にまとめる
cat *.json | jq -s '.'                             # 各ファイルを配列要素に
cat *.json | jq -s 'add'                           # 全配列を結合

# --null-input (-n) — 標準入力を読まず変数だけで構築
jq -n --arg name 'iigtn' '{user: $name}'

# 変数(--arg / --argjson)
jq --arg level 'error' '.events[] | select(.level == $level)' log.json
jq --argjson th 80 '.users[] | select(.score > $th)' data.json

# 環境変数
EVAL=prod jq -n 'env.EVAL'                        # "prod"

# 関数定義
jq '
def is_ops: .tags | contains(["ops"]);
.users[] | select(is_ops)
' data.json

# 再帰下降 (..)
echo '{"a":{"b":{"c":1}}}' | jq '.. | numbers?'    # 全 number 列挙

AWS / k8s 実戦レシピ

レシピ 1 — EC2 instance を Tag フィルタで抽出

aws ec2 describe-instances \
  | jq -r '.Reservations[].Instances[]
      | select(.State.Name == "running")
      | select((.Tags // []) | map(select(.Key == "Env" and .Value == "prod")) | length > 0)
      | [.InstanceId, .InstanceType, .PrivateIpAddress] | @tsv'

レシピ 2 — IAM Role の信頼ポリシーから Principal を抽出

aws iam list-roles \
  | jq -r '.Roles[]
      | .AssumeRolePolicyDocument as $p
      | "\(.RoleName)\t\($p.Statement[].Principal | tostring)"'

レシピ 3 — CloudWatch Logs から ERROR メッセージを抽出

aws logs filter-log-events --log-group-name /aws/lambda/myfn \
  | jq -r '.events[]
      | select(.message | test("ERROR"))
      | "\(.timestamp / 1000 | strftime("%Y-%m-%d %H:%M:%S"))  \(.message)"'

レシピ 4 — kubectl Pod の resource 集計

kubectl get pods -A -o json \
  | jq -r '.items[]
      | .metadata.namespace as $ns
      | .spec.containers[]
      | "\($ns)\t\(.name)\t\(.resources.requests.cpu // "-")\t\(.resources.requests.memory // "-")"' \
  | column -t -s $'\t'

レシピ 5 — 複数 JSON ファイルから値を 1 個ずつ更新

# 各 JSON に property を追加して上書き保存
for f in configs/*.json; do
  jq '. + {"version":"1.2.3"}' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
done

レシピ 6 — CSV エクスポート

aws s3api list-buckets \
  | jq -r '.Buckets[] | [.Name, .CreationDate] | @csv'

# ヘッダ込み
aws s3api list-buckets \
  | jq -r '
    ["Name","CreationDate"],
    (.Buckets[] | [.Name, .CreationDate])
    | @csv'

レシピ 7 — 2 つの JSON を結合(join 相当)

# users.json と orders.json を user_id で join
jq -s '
  .[0] as $users | .[1] as $orders
  | $orders | map(. + ($users[] | select(.id == .user_id) | {user_name: .name}))
' users.json orders.json

コマンド早見表

# 基本
jq . file.json                                  # 整形
jq -r '.users[].name' file.json                 # raw 出力
jq -c '.' file.json                             # compact

# 絞り込み・変換
jq '.users[] | select(.score > 80)' file.json
jq '.users | map({id, name})' file.json

# 集計
jq '.users | group_by(.tags[0]) | map({tag: .[0].tags[0], count: length})'
jq '.users | map(.score) | (add / length)'      # 平均

# 形式変換
jq 'to_entries / from_entries'                  # オブジェクト ↔ 配列
jq -r '.users[] | [.id, .name] | @tsv'          # TSV
jq -r '.users[] | [.id, .name] | @csv'          # CSV

# 複数入力
jq -s 'add' *.json                              # 全配列結合
jq -n --arg k v '{key: $k, value: $v}'          # null input から構築

用語集

jq
JSON 用のフィルタ言語兼処理コマンド。C 製、軽量・高速。GitHub の jqlang/jq がメンテ。
identity (.)
jq の最も基本のフィルタ「入力をそのまま返す」。整形だけしたい時 jq .
パイプ (|)
左の出力を右の入力に渡す jq の演算子。シェルの | と概念は近い。
配列展開 [.]
.users[] は「配列を展開して各要素をストリームとして次に流す」。map(...) で配列のまま処理することも可能。
raw 出力 (-r)
文字列の出力時にクォートを取り除く。bash 変数代入やファイル名生成に必須。
slurp (-s)
標準入力の全 JSON を 1 配列にまとめてから処理する。複数ファイル / NDJSON の集計で使う。
null input (-n)
標準入力を読まず、純粋に式と変数だけで JSON を構築するモード。
--arg / --argjson
外部から jq 内変数に値を渡す。--arg は文字列、--argjson は JSON として解釈。
select / map
select は条件で絞り込み(要素を消すかそのまま)、map は配列の各要素に式を適用して配列を返す。
group_by / sort_by / min_by / max_by
計算式に基づくグループ化・ソート・最小最大選出。
reduce
「初期値からアキュムレータを使って畳み込む」関数型の集計操作。合計や辞書化で使う。
to_entries / from_entries / with_entries
オブジェクトと {key,value} 配列の相互変換。AWS Tag のフォーマット変換に超頻出。
文字列補間 \(.x)
jq の文字列内で式を埋め込む構文。"\(.id): \(.name)" でフォーマット出力。
@tsv / @csv / @json / @sh / @uri
配列を特定形式へ変換する出力フィルタ。jq -r '.x | @csv'
JMESPath との違い
AWS CLI の --query は JMESPath。集計が苦手なので、複雑な処理は jq に渡すのが現場の定番。
関数定義 (def)
jq 内で再利用できる関数を def name: ...; で定義可能。複雑なクエリの可読性向上に。