「curl で取得した API レスポンスから特定のフィールドだけ抜き出したい」「package.json の依存関係を一覧にしたい」「JSON 形式のログからエラーだけ抽出したい」
こういう作業をターミナルで手軽にこなせるのが jq だ。JSON のスイスアーミーナイフとも呼ばれ、整形・フィルタ・変換・集計まで1つのコマンドでカバーする。
この記事では、基本的なフィルタから実践的なスクリプト活用まで、実際に動かせるコマンド例を交えて解説する。
jq とは
jq はコマンドラインで JSON を処理するための軽量ツールだ。C 言語で書かれていて、外部依存なしの単一バイナリで動作する。
主な特徴:
- JSON 整形 — ミニファイされた JSON を見やすくフォーマット
- フィールド抽出 — 必要な値だけピンポイントで取り出す
- フィルタリング — 条件に合うデータだけ選別
- 変換・集計 — データ形式の組み替えや計算
- パイプライン — 複数の処理をつなげて複雑な加工も1行で
sed や awk がテキスト処理の定番ツールであるように、jq は JSON 処理の定番だ。API を叩く機会が増えた今、curl と並んで必須のツールと言える。
インストール
# winget(推奨)
winget install jqlang.jq
# WSL を使っている場合は Linux と同じ
# sudo apt install jq
インストールできたらバージョンを確認しよう。
jq --version
jq-1.8.1
基本的な使い方
JSON を整形する
最もシンプルな使い方は . フィルタだ。入力された JSON をそのまま整形して出力する。
echo '{"name":"test","value":42,"active":true}' | jq '.'
{
"name": "test",
"value": 42,
"active": true
}
ファイルから読み込む場合はファイル名を引数に渡す。
jq '.' data.json
フィールドを取得する
ドット記法で特定のフィールドを取り出せる。
echo '{"name":"jq","version":"1.8.1"}' | jq '.name'
"jq"
クォートなしの生の文字列が欲しい場合は -r(raw output)を使う。
echo '{"name":"jq","version":"1.8.1"}' | jq -r '.name'
jq
ネストしたフィールド
ドットをつなげてネストの深い値にアクセスできる。
echo '{"data":{"users":[{"name":"Alice","age":30}]}}' | jq '.data.users[0].name'
"Alice"
配列操作
# 最初の要素
echo '[10,20,30]' | jq '.[0]'
# 最後の要素
echo '[10,20,30]' | jq '.[-1]'
# 配列の長さ
echo '[10,20,30]' | jq 'length'
# スライス(インデックス1から2まで)
echo '[10,20,30,40]' | jq '.[1:3]'
出力形式の制御
# コンパクト出力(1行に圧縮)
echo '{"name":"test","value":42}' | jq -c '.'
# タブインデント
echo '{"name":"test","value":42}' | jq --tab '.'
# ソート済みキーで出力
echo '{"b":2,"a":1}' | jq -S '.'
フィルタとパイプ
jq の真価はフィルタの組み合わせにある。パイプ | でフィルタをつなげて、複雑な処理を組み立てられる。
配列イテレータ
[] で配列の各要素を展開する。
echo '{"users":[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]}' \
| jq '.users[].name'
"Alice"
"Bob"
"Charlie"
select — 条件フィルタ
select() で条件に合う要素だけ抽出する。
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25},{"name":"Charlie","age":35}]}' \
| jq '.users[] | select(.age > 28)'
{
"name": "Alice",
"age": 30
}
{
"name": "Charlie",
"age": 35
}
map — 配列の変換
map() は配列の各要素にフィルタを適用して、新しい配列を返す。
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
| jq '.users | map(.name)'
[
"Alice",
"Bob"
]
オブジェクトの構築
必要なフィールドだけ抜き出して新しいオブジェクトを作れる。
echo '{"users":[{"name":"Alice","age":30,"email":"alice@example.com","role":"admin"}]}' \
| jq '.users[] | {name: .name, email: .email}'
{
"name": "Alice",
"email": "alice@example.com"
}
文字列補間
\() で値を文字列に埋め込める。-r と組み合わせるのが定番だ。
echo '{"users":[{"name":"Alice","email":"alice@example.com"},{"name":"Bob","email":"bob@example.com"}]}' \
| jq -r '.users[] | "\(.name): \(.email)"'
Alice: alice@example.com
Bob: bob@example.com
条件分岐
echo '{"status":"ok","data":"hello"}' \
| jq 'if .status == "ok" then .data else "error: \(.status)" end'
"hello"
ソート・グループ化・集計
# ソート
echo '[{"name":"Charlie","age":35},{"name":"Alice","age":30},{"name":"Bob","age":25}]' \
| jq 'sort_by(.age)'
# ユニーク
echo '["apple","banana","apple","cherry","banana"]' | jq 'unique'
# 合計
echo '[10,20,30,40]' | jq 'add'
# 平均値
echo '[10,20,30,40]' | jq 'add / length'
keys と has
# オブジェクトのキー一覧
echo '{"name":"test","version":"1.0","license":"MIT"}' | jq 'keys'
# キーの存在確認
echo '{"name":"test"}' | jq 'has("name")'
実践ユースケース
API レスポンスの加工
curl と組み合わせて API レスポンスから必要な情報を抽出するのは、jq の最も一般的な使い方だ。
curl -s https://api.github.com/repos/jqlang/jq \
| jq '{name: .name, stars: .stargazers_count, forks: .forks_count, license: .license.spdx_id}'
{
"name": "jq",
"stars": 31000,
"forks": 1600,
"license": "MIT"
}
ページネーション付きの API で複数ページの結果を結合する場合:
for page in 1 2 3; do
curl -s "https://api.github.com/users/octocat/repos?per_page=100&page=${page}"
done | jq -s 'flatten | map({name: .name, stars: .stargazers_count}) | sort_by(.stars) | reverse'
package.json の解析
プロジェクトの依存関係をざっと確認したいときに便利だ。
# 依存パッケージの一覧
jq '.dependencies | keys' package.json
[
"next",
"react",
"react-dom"
]
# パッケージ名とバージョンを見やすく表示
jq -r '.dependencies | to_entries[] | "\(.key)@\(.value)"' package.json
next@^16.0.0
react@^19.0.0
react-dom@^19.0.0
# dependencies と devDependencies の合計数
jq '{deps: (.dependencies | length), devDeps: (.devDependencies | length)}' package.json
ログファイルの JSON 解析
JSON Lines 形式(1行1JSON)のログファイルを解析する。
# エラーログだけ抽出してタイムスタンプとメッセージを表示
cat app.log \
| jq -r 'select(.level == "error") | "\(.timestamp) [\(.level)] \(.message)"'
2026-03-08T10:15:30Z [error] Database connection timeout
2026-03-08T10:18:45Z [error] Failed to process request
# エラーレベルごとの件数を集計
cat app.log \
| jq -s 'group_by(.level) | map({level: .[0].level, count: length})'
[
{ "level": "error", "count": 5 },
{ "level": "info", "count": 142 },
{ "level": "warn", "count": 23 }
]
設定ファイルの操作
JSON 形式の設定ファイルをコマンドラインから編集できる。
# 値の更新
jq '.database.port = 5433' config.json > tmp.json && mv tmp.json config.json
# フィールドの追加
jq '.database.ssl = true' config.json > tmp.json && mv tmp.json config.json
# フィールドの削除
jq 'del(.debug)' config.json > tmp.json && mv tmp.json config.json
# 2つの JSON ファイルをマージ(override が優先)
jq -s '.[0] * .[1]' base.json override.json > merged.json
スクリプト活用例
GitHub リポジトリ情報を CSV に変換
#!/bin/bash
# github-repos-to-csv.sh
# 指定ユーザーの公開リポジトリ情報をCSVに変換する
USERNAME="${1:?Usage: $0 <github-username>}"
OUTPUT="repos.csv"
echo "name,stars,forks,language,updated" > "${OUTPUT}"
page=1
while true; do
response=$(curl -s "https://api.github.com/users/${USERNAME}/repos?per_page=100&page=${page}")
count=$(echo "${response}" | jq 'length')
if [ "${count}" -eq 0 ]; then
break
fi
echo "${response}" \
| jq -r '.[] | [.name, .stargazers_count, .forks_count, (.language // "N/A"), .updated_at[:10]] | @csv' \
>> "${OUTPUT}"
page=$((page + 1))
done
total=$(tail -n +2 "${OUTPUT}" | wc -l)
echo "Exported ${total} repositories to ${OUTPUT}"
ポイント:
@csvフィルタが CSV フォーマットへの変換を自動処理する(クォートやエスケープ含む)// "N/A"は alternative operator で、nullの場合にデフォルト値を返す- ページネーションをループで処理して全リポジトリを取得する
複数 JSON ファイルの結合と集計レポート
#!/bin/bash
# merge-json-reports.sh
# reports/ ディレクトリ内のJSONレポートを結合して集計する
REPORT_DIR="${1:?Usage: $0 <report-directory>}"
if [ ! -d "${REPORT_DIR}" ]; then
echo "Error: Directory '${REPORT_DIR}' not found" >&2
exit 1
fi
file_count=$(find "${REPORT_DIR}" -name "*.json" -type f | wc -l)
if [ "${file_count}" -eq 0 ]; then
echo "Error: No JSON files found in '${REPORT_DIR}'" >&2
exit 1
fi
# 全JSONファイルを結合して集計
find "${REPORT_DIR}" -name "*.json" -type f -exec cat {} + \
| jq -s '{
total_files: length,
total_records: (map(.records // 0) | add),
total_errors: (map(.errors // 0) | add),
avg_duration_ms: (map(.duration_ms // 0) | add / length | floor),
statuses: (group_by(.status) | map({status: .[0].status, count: length})),
date_range: {
earliest: (map(.timestamp) | sort | first),
latest: (map(.timestamp) | sort | last)
}
}'
echo ""
echo "Processed ${file_count} files from ${REPORT_DIR}"
ポイント:
-s(slurp)で複数の JSON を1つの配列にまとめるgroup_byとmapの組み合わせでステータス別集計// 0でフィールドが存在しない場合のエラーを防ぐfloorで小数点以下を切り捨て
セキュリティの注意点
信頼できない JSON を処理する場合は、以下の点に注意してほしい。
- バージョンを最新に保つ — パーサーの脆弱性は定期的に発見される。
jq --versionで現在のバージョンを確認し、1.8.1 未満なら即アップデート - 入力サイズを制限する — 巨大な JSON を処理するとメモリを大量消費する。パイプラインの前段で
head -cなどでサイズを制限するか、ストリーミングパーサー(--stream)を検討する - シェルインジェクションに注意 — jq の出力をそのまま
evalやbash -cに渡すのは危険だ。必ずjq -rで取り出した値をクォートして使う
# 危険な例(絶対にやらないこと)
eval $(curl -s https://example.com/config.json | jq -r '.command')
# 安全な例(変数に代入してクォート)
value=$(curl -s https://example.com/config.json | jq -r '.setting')
echo "Setting: ${value}"
まとめ
jq は JSON をターミナルで自在に操るための必須ツールだ。
- 基本 —
.で整形、.fieldで抽出、-rで生文字列出力 - フィルタ —
selectで条件抽出、mapで変換、パイプで組み合わせ - 実践 — API レスポンス加工、設定ファイル編集、ログ解析
- スクリプト —
@csv変換、-sで結合、シェルスクリプトへの組み込み
curl でデータを取得し、jq で加工し、sed・awk でテキスト整形するという流れを身につければ、ターミナルでのデータ処理が格段に効率化する。まずは curl ... | jq '.' から始めてみてほしい。
CLI ツールの全体像は CLIツール完全マップ も参考にしてほしい。