32blogby Studio Mitsu

jqコマンド完全ガイド:JSONをターミナルで自在に操る

jqの基本フィルタからAPI応答加工・設定ファイル操作・スクリプト組み込みまで、実例付きで徹底解説。

by omitsu21 min read
目次

「curl で取得した API レスポンスから特定のフィールドだけ抜き出したい」「package.json の依存関係を一覧にしたい」「JSON 形式のログからエラーだけ抽出したい」

こういう作業をターミナルで手軽にこなせるのが jq だ。JSON のスイスアーミーナイフとも呼ばれ、整形・フィルタ・変換・集計まで1つのコマンドでカバーする。

この記事では、基本的なフィルタから実践的なスクリプト活用まで、実際に動かせるコマンド例を交えて解説する。

jq とは

jqはコマンドラインで JSON を処理するための軽量ツールだ。C 言語で書かれていて、外部依存なしの単一バイナリで動作する。GitHub リポジトリでソースコードが公開されており、MIT ライセンスで利用できる。

主な特徴:

  • JSON 整形 — ミニファイされた JSON を見やすくフォーマット
  • フィールド抽出 — 必要な値だけピンポイントで取り出す
  • フィルタリング — 条件に合うデータだけ選別
  • 変換・集計 — データ形式の組み替えや計算
  • パイプライン — 複数の処理をつなげて複雑な加工も1行で

sedawk がテキスト処理の定番ツールであるように、jq は JSON 処理の定番だ。API を叩く機会が増えた今、curl と並んで必須のツールと言える。

インストール

powershell
# winget(推奨)
winget install jqlang.jq

# WSL を使っている場合は Linux と同じ
# sudo apt install jq

インストールできたらバージョンを確認しよう。

bash
jq --version
text
jq-1.8.1

基本的な使い方

JSON を整形する

最もシンプルな使い方は . フィルタだ。入力された JSON をそのまま整形して出力する。

bash
echo '{"name":"test","value":42,"active":true}' | jq '.'
json
{
  "name": "test",
  "value": 42,
  "active": true
}

ファイルから読み込む場合はファイル名を引数に渡す。

bash
jq '.' data.json

フィールドを取得する

ドット記法で特定のフィールドを取り出せる。

bash
echo '{"name":"jq","version":"1.8.1"}' | jq '.name'
text
"jq"

クォートなしの生の文字列が欲しい場合は -r(raw output)を使う。

bash
echo '{"name":"jq","version":"1.8.1"}' | jq -r '.name'
text
jq

ネストしたフィールド

ドットをつなげてネストの深い値にアクセスできる。

bash
echo '{"data":{"users":[{"name":"Alice","age":30}]}}' | jq '.data.users[0].name'
text
"Alice"

配列操作

bash
# 最初の要素
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]'

出力形式の制御

bash
# コンパクト出力(1行に圧縮)
echo '{"name":"test","value":42}' | jq -c '.'

# タブインデント
echo '{"name":"test","value":42}' | jq --tab '.'

# ソート済みキーで出力
echo '{"b":2,"a":1}' | jq -S '.'

フィルタとパイプ

jq の真価はフィルタの組み合わせにある。パイプ | でフィルタをつなげて、複雑な処理を組み立てられる。

配列イテレータ

[] で配列の各要素を展開する。

bash
echo '{"users":[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]}' \
  | jq '.users[].name'
text
"Alice"
"Bob"
"Charlie"

select — 条件フィルタ

select() で条件に合う要素だけ抽出する。

bash
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25},{"name":"Charlie","age":35}]}' \
  | jq '.users[] | select(.age > 28)'
json
{
  "name": "Alice",
  "age": 30
}
{
  "name": "Charlie",
  "age": 35
}

map — 配列の変換

map() は配列の各要素にフィルタを適用して、新しい配列を返す。

bash
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq '.users | map(.name)'
json
[
  "Alice",
  "Bob"
]

オブジェクトの構築

必要なフィールドだけ抜き出して新しいオブジェクトを作れる。

bash
echo '{"users":[{"name":"Alice","age":30,"email":"alice@example.com","role":"admin"}]}' \
  | jq '.users[] | {name: .name, email: .email}'
json
{
  "name": "Alice",
  "email": "alice@example.com"
}

文字列補間

\() で値を文字列に埋め込める。-r と組み合わせるのが定番だ。

bash
echo '{"users":[{"name":"Alice","email":"alice@example.com"},{"name":"Bob","email":"bob@example.com"}]}' \
  | jq -r '.users[] | "\(.name): \(.email)"'
text
Alice: alice@example.com
Bob: bob@example.com

条件分岐

bash
echo '{"status":"ok","data":"hello"}' \
  | jq 'if .status == "ok" then .data else "error: \(.status)" end'
text
"hello"

ソート・グループ化・集計

bash
# ソート
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

bash
# オブジェクトのキー一覧
echo '{"name":"test","version":"1.0","license":"MIT"}' | jq 'keys'

# キーの存在確認
echo '{"name":"test"}' | jq 'has("name")'

実践ユースケース

API レスポンスの加工

curl と組み合わせて API レスポンスから必要な情報を抽出するのは、jq の最も一般的な使い方だ。

bash
curl -s https://api.github.com/repos/jqlang/jq \
  | jq '{name: .name, stars: .stargazers_count, forks: .forks_count, license: .license.spdx_id}'
json
{
  "name": "jq",
  "stars": 31000,
  "forks": 1600,
  "license": "MIT"
}

ページネーション付きの API で複数ページの結果を結合する場合:

bash
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 の解析

プロジェクトの依存関係をざっと確認したいときに便利だ。

bash
# 依存パッケージの一覧
jq '.dependencies | keys' package.json
json
[
  "next",
  "react",
  "react-dom"
]
bash
# パッケージ名とバージョンを見やすく表示
jq -r '.dependencies | to_entries[] | "\(.key)@\(.value)"' package.json
text
next@^16.0.0
react@^19.0.0
react-dom@^19.0.0
bash
# dependencies と devDependencies の合計数
jq '{deps: (.dependencies | length), devDeps: (.devDependencies | length)}' package.json

ログファイルの JSON 解析

JSON Lines 形式(1行1JSON)のログファイルを解析する。

bash
# エラーログだけ抽出してタイムスタンプとメッセージを表示
cat app.log \
  | jq -r 'select(.level == "error") | "\(.timestamp) [\(.level)] \(.message)"'
text
2026-03-08T10:15:30Z [error] Database connection timeout
2026-03-08T10:18:45Z [error] Failed to process request
bash
# エラーレベルごとの件数を集計
cat app.log \
  | jq -s 'group_by(.level) | map({level: .[0].level, count: length})'
json
[
  { "level": "error", "count": 5 },
  { "level": "info", "count": 142 },
  { "level": "warn", "count": 23 }
]

設定ファイルの操作

JSON 形式の設定ファイルをコマンドラインから編集できる。

bash
# 値の更新
jq '.database.port = 5433' config.json > tmp.json && mv tmp.json config.json
bash
# フィールドの追加
jq '.database.ssl = true' config.json > tmp.json && mv tmp.json config.json
bash
# フィールドの削除
jq 'del(.debug)' config.json > tmp.json && mv tmp.json config.json
bash
# 2つの JSON ファイルをマージ(override が優先)
jq -s '.[0] * .[1]' base.json override.json > merged.json

応用テクニック

シェル変数を安全に渡す(--arg / --argjson)

jq のフィルタ内でシェル変数を使いたい場面は多い。直接文字列補間すると壊れやすいので、--arg(文字列)と --argjson(数値・配列・オブジェクト)を使う。

bash
# --arg: 文字列として渡す
username="Alice"
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq --arg name "${username}" '.users[] | select(.name == $name)'
json
{
  "name": "Alice",
  "age": 30
}
bash
# --argjson: 数値や配列として渡す(クォートされない)
min_age=28
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq --argjson min "${min_age}" '.users[] | select(.age >= $min)'
bash
# 動的にJSONオブジェクトを構築する
key="version"
value="2.0"
jq -n --arg k "${key}" --arg v "${value}" '{($k): $v}'
json
{
  "version": "2.0"
}

--arg は常に文字列として渡す。数値として比較したい場合は --argjson を使うこと。--arg で渡した "28".age(数値の 28)は型が異なるので select(.age >= $min) が期待通りに動かない。

エラーハンドリング(try-catch / ? 演算子)

不完全な JSON や欠損フィールドを処理するときに使う。

bash
# ? 演算子: エラーを無視して処理を続行(try の省略形)
echo 'null' | jq '.foo?'

.foo?null に対してフィールドアクセスエラーを出さず、null を返す。パイプライン中でデータ構造が不定なとき便利だ。

bash
# // 演算子: null や false のときフォールバック値を返す
echo '{"a":1} {"b":2} {"a":3}' | jq '.a // empty'
text
1
3
bash
# try-catch: エラー時にフォールバック値を返す
echo '{"data":"not-a-number"}' \
  | jq 'try (.data | tonumber) catch "parse error"'
text
"parse error"
bash
# 配列の要素にフィールドがあったりなかったりする場合
echo '[{"name":"Alice","email":"a@example.com"},{"name":"Bob"}]' \
  | jq '.[] | {name, email: (.email // "N/A")}'
json
{
  "name": "Alice",
  "email": "a@example.com"
}
{
  "name": "Bob",
  "email": "N/A"
}

フォーマット文字列(@base64 / @uri / @html / @tsv)

jq には出力を特定フォーマットに変換するビルトインフォーマッタがある。

bash
# @base64: Base64エンコード
echo '{"token":"hello:world"}' | jq -r '.token | @base64'
text
aGVsbG86d29ybGQ=
bash
# @base64d: Base64デコード
echo '"aGVsbG86d29ybGQ="' | jq -r '@base64d'
text
hello:world
bash
# @uri: URLエンコード(クエリパラメータの構築に便利)
echo '{"q":"jq filter examples","lang":"ja"}' \
  | jq -r '"https://example.com/search?q=\(.q | @uri)&lang=\(.lang)"'
text
https://example.com/search?q=jq%20filter%20examples&lang=ja
bash
# @tsv: 配列をTSV(タブ区切り)に変換
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' \
  | jq -r '.[] | [.name, .age] | @tsv'
text
Alice	30
Bob	25

巨大 JSON のストリーミング処理(--stream)

数百 MB 以上の JSON ファイルを jq '.' で処理するとメモリに全体を読み込む。--stream を使えば、パス・値のペアとして逐次処理できる。

bash
# --stream: 逐次処理(メモリを節約)
echo '{"users":[{"name":"Alice"},{"name":"Bob"}]}' \
  | jq --stream 'select(.[0][-1] == "name") | .[1]'
text
"Alice"
"Bob"
bash
# 巨大ファイルから特定キーだけ高速に抽出
jq --stream 'select(.[0][0] == "error_count") | .[1]' huge_report.json

ストリーミングモードはフィルタの書き方が通常と異なるので学習コストがあるが、通常モードでメモリ不足になる場合の選択肢として覚えておくと良い。

環境変数にアクセスする($ENV / env)

CI/CD パイプラインやスクリプトで、環境変数を JSON に組み込みたい場面で使える。

bash
export APP_VERSION="1.5.0"
export APP_ENV="production"

# $ENV で環境変数にアクセス
jq -n '{version: $ENV.APP_VERSION, env: $ENV.APP_ENV}'
json
{
  "version": "1.5.0",
  "env": "production"
}
bash
# env オブジェクトで全環境変数を確認
jq -n 'env | keys | map(select(startswith("APP_")))'
json
[
  "APP_ENV",
  "APP_VERSION"
]

$ENVenv の違い: $ENV.KEY は特定のキーに直接アクセスする。env はオブジェクトとして全環境変数を返すので keysselect でフィルタできる。

スクリプト活用例

jq をシェルスクリプトに組み込むと、JSON データの自動処理が格段に楽になる。xargs と組み合わせれば、抽出した値を別のコマンドに並列で渡すこともできる。

GitHub リポジトリ情報を CSV に変換

bash
#!/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 または false の場合にデフォルト値を返す
  • ページネーションをループで処理して全リポジトリを取得する

複数 JSON ファイルの結合と集計レポート

bash
#!/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_bymap の組み合わせでステータス別集計
  • // 0 でフィールドが存在しない場合のエラーを防ぐ
  • floor で小数点以下を切り捨て

セキュリティの注意点

信頼できない JSON を処理する場合は、以下の点に注意してほしい。

  • バージョンを最新に保つ — パーサーの脆弱性は定期的に発見される。jq --version で現在のバージョンを確認し、1.8.1 未満なら即アップデート
  • 入力サイズを制限する — 巨大な JSON を処理するとメモリを大量消費する。パイプラインの前段で head -c などでサイズを制限するか、ストリーミングパーサー(--stream)を検討する
  • シェルインジェクションに注意 — jq の出力をそのまま evalbash -c に渡すのは危険だ。必ず jq -r で取り出した値をクォートして使う
bash
# 危険な例(絶対にやらないこと)
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}"

関連記事

FAQ

jq と yq の違いは?

jq は JSON 専用のプロセッサだ。yq は YAML・XML・TOML も扱えるツールで、内部的に jq ライクな構文を採用している。JSON だけ扱うなら jq のほうが高速で軽量。YAML や複数フォーマットを扱う必要があるなら yq を検討しよう。

jq をインストールせずに使う方法はある?

jq play というオンラインプレイグラウンドがある。ブラウザ上でフィルタを試せるので、インストール前の学習や、複雑なフィルタのデバッグに便利だ。ただし機密データは入力しないこと。

jq で巨大な JSON ファイルを処理するとメモリ不足になる。対処法は?

--stream オプションを使うと、JSON をパス・値のペアとして逐次処理できるためメモリ消費を抑えられる。フィルタの書き方が通常と異なるが、数百 MB 以上のファイルを扱うときは有効な手段だ。

jq の出力を CSV に変換するには?

@csv フォーマッタを使う。配列を作って @csv に渡すだけだ:jq -r '.[] | [.name, .age] | @csv'。クォートやエスケープも自動で処理してくれる。TSV にしたい場合は @tsv を使う。

jq フィルタの中でシェル変数を使うには?

--arg(文字列)または --argjson(数値・配列・オブジェクト)を使う。直接文字列補間すると壊れやすいので、必ずこれらのオプション経由で渡すこと。数値比較には --argjson を使わないと型不一致でフィルタが期待通りに動かない。

jq で null や欠損フィールドを安全に処理するには?

// 演算子(alternative operator)でフォールバック値を指定する:.email // "N/A"? 演算子でエラーを抑制することもできる:.foo?try-catch 構文も使える:try (.data | tonumber) catch "parse error"

Python の json モジュールと jq、どちらを使うべき?

ワンライナーやシェルスクリプトでの加工なら jq が圧倒的に速い。複雑なロジック(条件分岐が多い、DB連携がある等)なら Python のほうが可読性が高い。CI/CD パイプラインでは依存関係が少ない jq が好まれる。

まとめ

jq は JSON をターミナルで自在に操るための必須ツールだ。

  • 基本. で整形、.field で抽出、-r で生文字列出力
  • フィルタselect で条件抽出、map で変換、パイプで組み合わせ
  • 実践 — API レスポンス加工、設定ファイル編集、ログ解析
  • スクリプト@csv 変換、-s で結合、シェルスクリプトへの組み込み

curl でデータを取得し、jq で加工し、sed・awk でテキスト整形するという流れを身につければ、ターミナルでのデータ処理が格段に効率化する。まずは curl ... | jq '.' から始めてみてほしい。

関連記事: