この記事の要点
• jq '.'でJSON整形、jq '.key'で値抽出が基本
• selectで条件フィルタ、mapで一括変換
• -rオプションでクォートなしのraw出力(スクリプト連携に必須)
概要
jq は JSON データを変換・抽出するためのコマンドラインツールです。sed や awk が行指向のテキストに対して行う処理を、jq は構造化された JSON に対して行います。HTTP API のレスポンス整形、設定ファイルの加工、ログ解析、CI スクリプトでの値抽出など幅広く使われています。本チートシートは現行版 jq 1.7 系を対象に、よく使う構文と実用レシピを整理します。
基本コマンド
| コマンド | 説明 |
|---|---|
jq '.' file.json | 整形表示 |
cat file.json | jq '.' | パイプから読む |
curl -s https://api.example.com | jq '.users[0]' | API レスポンスから値抽出 |
jq -n '{a:1, b:2}' | 入力なしで生成 |
jq -r '.name' file.json | raw 出力(クォートなし) |
主要オプション
| オプション | 説明 |
|---|---|
-r, --raw-output | 文字列を生のテキストとして出力(クォート除去) |
-R, --raw-input | 入力を 1 行ずつ文字列として読む |
-s, --slurp | 入力全体を 1 つの配列として読む |
-c, --compact-output | 1 行コンパクト出力 |
-n, --null-input | 入力を読まず null から開始 |
-a, --ascii-output | 非 ASCII を \uXXXX でエスケープ |
-S, --sort-keys | キーをソートして出力 |
-e, --exit-status | 出力が null/false なら終了コード 1 |
--arg name value | 文字列変数を渡す |
--argjson name json | JSON 変数を渡す |
--slurpfile name file | ファイルを配列で読み込んで変数化 |
--rawfile name file | ファイルを文字列で読み込んで変数化 |
-C / -M | カラー / モノクロ強制 |
ポイント: -r(raw出力)は変数代入時に必須。クォートが残るとシェルスクリプトで予期しない動作になります。
基本フィルタ
| 式 | 説明 |
|---|---|
. | 入力そのもの |
.foo | キー foo の値 |
.foo.bar | ネストしたキー |
.foo? | キーがなくてもエラーにしない |
.["foo bar"] | 特殊文字を含むキー |
.[0] | 配列の最初の要素 |
.[-1] | 配列の最後の要素 |
.[2:5] | 配列スライス |
.[] | 配列・オブジェクトを展開(イテレート) |
.foo, .bar | 複数の値を出力 |
.foo | .bar | パイプで連結 |
(.a + .b) | 式のグルーピング |
値の生成
1, 2, 3 # 3 つの値
"hello" # 文字列
true, false, null # 真偽値・null
[1, 2, 3] # 配列リテラル
{a: 1, b: 2} # オブジェクトリテラル
{user, id: .uid} # ショートハンド(user: .user)
実践メモ: .foo?のように?を付けるとキーが存在しなくてもエラーになりません。安全アクセスパターンとして覚えておきましょう。
配列操作
| 式 | 説明 |
|---|---|
length | 配列・オブジェクト・文字列の長さ |
reverse | 配列を逆順に |
sort | 昇順ソート |
sort_by(.age) | キーでソート |
unique | 重複削除+ソート |
unique_by(.id) | キーで重複削除 |
min, max | 最小・最大 |
min_by(.age), max_by(.age) | キーで最小・最大 |
add | 要素を全部足す(concat にもなる) |
flatten | 1 段階フラット化 |
flatten(2) | 2 段階フラット化 |
range(N) | 0..N-1 の数列 |
range(0;10;2) | 0..8 を 2 刻み |
[.[] | .name] | name フィールドの配列を作る |
map(.name) | 上と等価 |
map_values(. + 1) | オブジェクト値に関数適用 |
select(.age >= 20) | 条件にマッチする要素のみ |
any, all | 真偽の論理 |
group_by(.dept) | キーでグループ化 |
オブジェクト操作
| 式 | 説明 |
|---|---|
keys | キー一覧(ソート) |
keys_unsorted | キー一覧(順序保持) |
values | 値一覧 |
to_entries | [{key,value}, ...] に変換 |
from_entries | to_entries の逆 |
with_entries(f) | エントリ単位で関数適用 |
has("key") | キーの存在判定 |
in({a:1}) | 入力がオブジェクトのキーかを判定 |
del(.foo) | キー削除 |
.foo // "default" | null/false ならデフォルト |
. + {x: 1} | マージ(浅い) |
. * {x: 1} | 再帰マージ |
ポイント: selectで条件フィルタ、mapで一括変換、group_byで集計。この3つがデータ加工の三種の神器です。
文字列操作
| 式 | 説明 |
|---|---|
tostring | 文字列化 |
tonumber | 数値化 |
ascii_downcase / ascii_upcase | 大文字小文字変換 |
ltrimstr("foo") / rtrimstr("bar") | 接頭・接尾文字列を削除 |
startswith("a") / endswith("z") | 接頭・接尾判定 |
contains("x") | 部分一致 |
split(",") | 文字列を配列に |
join(",") | 配列を文字列に |
test("regex") | 正規表現マッチ判定 |
match("regex") | マッチ情報を返す |
capture("(?<n>\\w+)") | 名前付きキャプチャ |
sub("a"; "b") | 1 回置換 |
gsub("a"; "b") | 全置換 |
\(expr) | 文字列補間 |
"User: \(.name) (\(.age))"
数値・型
| 式 | 説明 |
|---|---|
floor, ceil, round | 整数化 |
fabs | 絶対値 |
sqrt, pow(x;y) | 平方根・累乗 |
type | ”string”/“number”/“object”/… |
isnan, isinfinite | 特殊値判定 |
tonumber, tostring | 型変換 |
注意: |(パイプ)は値を次のフィルタに流し、,は同じ入力に複数の値を出力します。両者を混同しないように注意しましょう。
制御構文
if .age >= 20 then "adult" else "minor" end
# elif
if .x == 0 then "zero"
elif .x > 0 then "positive"
else "negative"
end
# try / catch
try .a.b.c catch "missing"
# 代入
.user.name = "Alice" # 上書き
.user.name |= ascii_upcase # 既存値に関数適用
.tags += ["new"] # 配列追加
.count += 1 # 数値加算
.user |= . + {active:true} # オブジェクト更新
# 削除
del(.user.password)
変数と束縛
. as $root | .users[] | {name, root_id: $root.id}
# 複数値の分解
.coord as [$x, $y] | $x + $y
{a, b} as {a: $a, b: $b} | $a * $b
reduce / foreach
# 配列の合計
reduce .[] as $x (0; . + $x)
# 累積配列
[foreach .[] as $x (0; . + $x; .)]
関数定義
def double: . * 2;
def addn(n): . + n;
def stats: {min: min, max: max, count: length};
[1,2,3] | map(double) | stats
実践メモ: --argで外部値を安全に渡せます。シェル変数をフィルタに直接埋め込むとインジェクションのリスクがあるため、必ず--argを使いましょう。
実用レシピ
1. 配列から特定フィールドだけ抽出
jq '[.users[] | {id, name}]' data.json
2. 条件で絞り込み
jq '.users[] | select(.age >= 20 and .active)' data.json
3. CSV 風出力
jq -r '.users[] | [.id, .name, .email] | @csv' data.json
jq -r '.users[] | [.id, .name] | @tsv' data.json
4. キーをソートして出力
jq -S '.' config.json
5. ネストの深いキーのデフォルト
jq '.user.profile.bio // "no bio"' user.json
6. 環境変数との連携
NAME="Alice" jq --arg name "$NAME" '.users[] | select(.name == $name)' data.json
7. JSON Lines を読む
# 既定で JSON Lines もそのまま読める
cat events.jsonl | jq 'select(.level == "error")'
# 1 配列にまとめる
jq -s '.' events.jsonl > events.json
8. オブジェクトを配列に展開
jq 'to_entries | map({k: .key, v: .value})' map.json
9. 値で集計
jq '[.events[] | .type] | group_by(.) | map({type: .[0], count: length})' data.json
10. ネストしたフィールドを更新
jq '.user.profile.bio = "updated"' user.json
jq '(.users[] | select(.id == 1)).active = false' data.json
11. 複数 JSON をマージ
jq -s '.[0] * .[1]' a.json b.json
12. 配列差分
jq -n --slurpfile a a.json --slurpfile b b.json '$a[0] - $b[0]'
13. キー名の一括変換
jq 'with_entries(.key |= ascii_downcase)' data.json
14. null や空文字を除外
jq '.users | map(select(.email != null and .email != ""))' data.json
15. JSON を Bash 変数に取り込む
NAME=$(jq -r '.user.name' data.json)
mapfile -t IDS < <(jq -r '.users[].id' data.json)
16. curl の API レスポンスから値抜き出し
curl -s https://api.github.com/repos/jqlang/jq | jq -r '.stargazers_count'
17. 配列要素の追加・削除
jq '.tags += ["new-tag"]' data.json
jq '.tags -= ["old-tag"]' data.json
jq 'del(.tags[1])' data.json
18. パスを取得
jq 'paths(scalars)' data.json
jq 'paths(. == "target")' data.json
19. 再帰的に値を探す
jq '.. | objects | select(has("email")) | .email' data.json
20. テンプレ的な文字列生成
jq -r '.users[] | "\(.id),\(.name),\(.email)"' data.json
よく使うフォーマット出力
| 演算子 | 説明 |
|---|---|
@text | 文字列としてそのまま |
@json | JSON 文字列にエンコード |
@csv | CSV 行(要素は配列) |
@tsv | TSV 行 |
@sh | シェル安全クォート |
@base64 | Base64 エンコード |
@base64d | Base64 デコード |
@uri | URI エスケープ |
@html | HTML エスケープ |
注意: jqは内部でdoubleを使うため、大きな整数ID(例: Snowflake ID)は精度がロスします。文字列として扱うか、tostringで変換しましょう。
トラブルシューティング
| 症状 | 原因と対処 |
|---|---|
Cannot index array with string "foo" | 配列に対してキー参照している。.[] | .foo のように展開する |
null (null) and string ("x") cannot be added | null チェックを // "" で行う |
| 出力にダブルクォートが付く | -r を付けて raw 出力にする |
| 複数 JSON を 1 つにまとめたい | -s(slurp)で配列化 |
| エラーで終了したい | -e で値が null/false の場合に終了コード 1 |
| 大きな数値の精度ロス | jq は内部で double を使う。整数 ID は文字列で扱う |
Bash の $ 展開と競合 | フィルタを 'シングルクォート' で囲む |
| Windows で日本語が文字化け | chcp 65001 で UTF-8 に切り替え |
Tips & ベストプラクティス
- フィルタは必ずシングルクォートで囲んでシェル展開を防ぐ。
- 値だけ欲しいときは
-rを忘れない(特に変数代入時)。 - 複数行の複雑なフィルタは
defで関数化して読みやすくする。 - パイプライン
|は左から右へ値を流す。,は同じ入力に対して複数の値を出力する。両者を混同しないこと。 - 安全アクセスは
?を活用する(.a.b?.c?)。 - スクリプトに埋め込むときは
--arg/--argjsonで値を渡し、文字列連結による注入を避ける。 - 集計は
group_by→mapパターンで簡潔に書ける。 - フォーマット出力は
@csv/@tsvを使えば、エスケープを自分で書く必要がない。 - 1.7 系では SQL 風の
ltrimstr/rtrimstr、改善された I/O オプションが入っている。最新版を使うのが安全。 - フィルタを試すときは jqplay.org の利用が便利。
さらに進んだ機能
入力ストリーム関数
| 関数 | 説明 |
|---|---|
input | 次の入力を読む |
inputs | 残りすべての入力をストリームで返す |
input_filename | 現在処理中のファイル名 |
input_line_number | 入力行番号 |
debug | デバッグメッセージを stderr に出して値を素通し |
debug("label") | ラベル付きデバッグ |
stderr | stderr に出力して値を素通し |
error("msg") | エラーで停止 |
halt | エラーなしで終了 |
halt_error(N) | 終了コード N で終了 |
# 複数 JSON を順に処理
jq -n 'inputs | select(.level == "error")' a.json b.json c.json
# パイプの中間値をデバッグ
jq '.users | map(. | debug("user")) | length' data.json
パスと割り当て
# 値を持つすべてのパスを列挙
[paths]
# スカラー値のパスのみ
[paths(scalars)]
# 特定値を持つパス
[paths(. == "target")]
# パスを使った代入
getpath(["a", "b"])
setpath(["a", "b"]; 42)
delpaths([["a", "b"], ["c"]])
walk 関数
walk(f) でツリー全体を再帰的に変換できます。
# 文字列値を全て大文字に
jq 'walk(if type == "string" then ascii_upcase else . end)' data.json
# null を空文字に置換
jq 'walk(if . == null then "" else . end)' data.json
env と $ENV
HOME_PATH=/srv jq -n '{home: env.HOME_PATH, all: $ENV}'
SQL 風スタイル関数
| 関数 | 説明 |
|---|---|
INDEX(.id) | id をキーとしたオブジェクトに変換 |
IN(...) | いずれかと等しい |
GROUP_BY(.dept) | グループ化(group_by のラッパー) |
UNIQUE_BY(.id) | 重複削除 |
jq '[.users[]] | INDEX(.id)' data.json
# => { "1": {...}, "2": {...} }
モジュール / インポート
jq はライブラリパスからモジュールを読み込めます。~/.jq または -L <path> で指定したディレクトリ配下に .jq ファイルを配置します。
# ~/.jq/lib.jq
def percent(total): . / total * 100;
jq 'import "lib" as lib; .count | lib::percent(100)' data.json
組み込み定義は include で取り込めます。