近年のLinuxサーバー管理やクラウド運用(AWS CLI、GitHub API、Kubernetes、Dockerコンテナログ)において、実務で扱うデータ形式は **JSONがデファクトスタンダード** となっています。しかし、シェルスクリプトでJSONを扱う際、従来の grep や sed 、 awk だけでパースしようとして正規表現の罠にハマったり、クォートのエスケープ地獄で苦戦した経験はありませんか?
こうした現場の悩みを一撃で解決するのが jq コマンド です。 jq は、コマンドライン上で複雑なJSONデータを高速かつ自由自在に整形・抽出・加工・集計できる専用のCLIツールです。本記事では、日常のターミナル作業からシェルスクリプトへの組み込みまで、実務で今すぐ役立つ 「最短構文早見表」「配列ループ処理」「selectによる条件フィルタ」「CSV変換」「curl連携の自動化」 を網羅して徹底解説します。
【実務早見表】jqコマンド頻出フィルター・ワンライナー一覧
現場で「今すぐコピペして使いたい」という場面に向けて、実務で頻出する主要構文とオプションの逆引き早見表を用意しました。
整形・キー抽出・配列・CSV変換の最短構文対比表
日常のサーバー運用やAPIレスポンスの確認で最もよく使うワンライナーの構文一覧です。
| 目的・やりたいこと | 最短コマンド例 | 解説・実務のポイント |
|---|---|---|
| JSONインデント整形 | jq '.' data.json | 最も基本の整形表示(Pretty-print)。カラー表示で階層が瞬時に把握可能。 |
| 特定キーの値抽出 | jq -r '.name' data.json | -r を付けてダブルクォートを排除し、シェル変数に代入できる生テキストを出力。 |
| 階層(ネスト)キー抽出 | jq -r '.user.profile.email' data.json | ドット記法で深い階層のキーをたどって安全に値を取得。 |
| 配列の一括展開 | jq -r '.items[].id' data.json | .[] で配列内の全要素を展開し、各要素の id を改行区切りで一覧表示。 |
| 配列の特定番号取得 | jq -r '.[0].title' data.json | インデックス指定(先頭は 0 、末尾は -1 )で特定要素をピンポイント抽出。 |
| 条件付きフィルタリング | jq '.[] | select(.status == "active")' | select() 関数で条件に一致したオブジェクトのみをフィルタリング。 |
| 特定キーだけの再構築 | jq '[.[] | {id, name: .user.name}]' | 必要なキーだけをピックアップして新しいJSONオブジェクト/配列を作成。 |
| 配列からCSVへの変換 | jq -r '.[] | [.id, .name] | @csv' | @csv フィルタでフラット化し、Excelやスプレッドシート用データに変換。 |
| 複数JSONの結合・マージ | jq -s 'add' file1.json file2.json | -s (slurp)で複数ファイルを単一配列として読み込み、加算結合。 |
主要オプション(-r, -c, -s, -e)の機能と使い分け
jq コマンドをシェルスクリプト内で活用する際、オプションの指定を誤るとバグや誤動作の原因になります。特に重要な4大オプションの役割を整理します。
| オプション | 名称・機能 | 実務での必須シーン |
|---|---|---|
-r | --raw-output(クォート除去) | シェル変数への代入時は必須! 文字列を囲むダブルクォートを取り除き、純粋な生文字列として出力します。 |
-c | --compact-output(1行圧縮) | ループ処理(while read)の必須オプション! オブジェクトや配列を改行なしのコンパクトな1行JSONとして出力します。 |
-s | --slurp(一括配列化) | 改行区切りのJSONログや複数ファイルを、ひとつの巨大な配列 [...] としてまとめて読み込みます。 |
-e | --exit-status(終了コード制御) | フィルタ結果が false または null の場合に終了ステータス 1 (エラー)を返します。Bashの if 分岐に最適。 |
特に実務で圧倒的に多用されるのが -r オプション です。たとえば NAME=$(jq '.name' user.json) と実行すると、変数には "Alice" というダブルクォートを含んだ文字列が格納されてしまいます。これに気付かず後続のファイルパスやAPI引数に /home/$NAME/data と渡すと、 /home/"Alice"/data という不正なパスになりエラーが発生します。シェル変数へ代入する際は **必ず -r を付与する** 癖をつけましょう。
jqコマンドの基本操作と整形表示
まずは jq の基本となる入力方法と、人間が読みやすい整形表示(Pretty-print)の仕組みから押さえていきましょう。
JSONのインデント整形とカラー表示(パイプ渡し / ファイル引数)
jq の最も基本的な構文は、フィルタに '.' (入力データをそのまま通過させる恒等フィルタ)を渡す記法です。ファイル名を直接指定する方法と、標準入力からパイプで受け取る方法の両方に対応しています。
# ファイルを直接指定して整形表示
jq '.' sample.json
# パイプ(標準入力)経由で渡して整形表示
cat sample.json | jq '.'
# curlで取得したAPIレスポンスを整形(UUOCを避けて直接渡す)
curl -s https://httpbin.org/json | jq '.'
ターミナルに出力する場合、 jq はデフォルトで構文に応じた色分け(カラー表示)を行いますが、リダイレクトでファイルに保存したり less コマンドにパイプした場合は自動的にカラーコードが抑制されます。パイプ先でも強制的にカラーを残したい場合は -C オプション、ログ保存などで絶対にエスケープシーケンスを含めたくない場合は -M (モノクロ強制)を指定します。
クォートなしで値を取り出す生テキスト出力(-r オプション)
シェルスクリプトで特定キーの値を抽出し、後続処理で変数として利用する場合は -r ( --raw-output )の併用が不可欠です。通常出力と生出力(raw output)の挙動の違いを比較してみましょう。
# サンプルJSONデータ
echo '{"service": "web-server", "port": 8080}' > server.json
# 通常出力:JSON文字列としてダブルクォートが付く
jq '.service' server.json
# 出力結果: "web-server"
# 生出力(-r):ダブルクォートが外れたプレーンテキスト
jq -r '.service' server.json
# 出力結果: web-server
# シェル変数への安全な代入例
SERVICE_NAME=$(jq -r '.service' server.json)
PORT_NUM=$(jq -r '.port' server.json)
echo "稼働サービス: ${SERVICE_NAME}(ポート: ${PORT_NUM})"
ネスト(階層構造)されたオブジェクトのドット記法アクセス
オブジェクトが何層にもネストしているJSONでは、ドット . で階層を連結してアクセスします。キー名にハイフン( - )やスペース、記号が含まれる場合は、ダブルクォートまたはブラケット記法でエスケープする必要があります。
# ネストされた設定JSONの例
cat < config.json
{
"database": {
"connection": {
"host": "db.example.internal",
"port": 5432
}
},
"app-settings": {
"max-workers": 16
}
}
EOF
# 通常のネストアクセス(ドット記法)
jq -r '.database.connection.host' config.json
# 出力: db.example.internal
# ハイフンを含むキー名はダブルクォートで囲む
jq -r '."app-settings"."max-workers"' config.json
# またはブラケット記法
jq -r '.["app-settings"]["max-workers"]' config.json
# 出力: 16
配列(Array)の展開とループ処理テクニック
JSONの実務処理で最も頻出するのが **配列データの展開とループ処理** です。 jq の配列展開構文をマスターすれば、大量のリストデータから必要な項目を一瞬で抽出できます。
配列要素の一括展開(.[])とインデックス指定(.[0])
配列全体を各要素ごとのストリーム(改行区切り)として展開するには .[] を使用します。特定の順番にある要素だけを取り出す場合はインデックス番号 .[n] を指定します。
# サーバー一覧の配列JSON
cat < servers.json
[
{"id": 101, "host": "web01", "ip": "192.168.1.10"},
{"id": 102, "host": "web02", "ip": "192.168.1.11"},
{"id": 103, "host": "db01", "ip": "192.168.1.20"}
]
EOF
# 配列要素のすべてを個別のオブジェクトとして展開
jq -c '.[]' servers.json
# 先頭要素(インデックス0)を取得
jq -r '.[0].host' servers.json
# 出力: web01
# 末尾要素(インデックス-1)を取得
jq -r '.[-1].host' servers.json
# 出力: db01
# 範囲スライス(0番目から2つ分)
jq -c '.[0:2]' servers.json
オブジェクト配列から特定キーのみをリスト抽出する
オブジェクト配列から特定キーだけをプレーンテキストで取り出し、他のLinuxコマンドに渡す処理はシステム管理の定番です。たとえば全サーバーのホスト名やIPアドレスだけを一覧抽出できます。
# 全サーバーのIPアドレス一覧を生テキストで取得
jq -r '.[].ip' servers.json
# 出力:
# 192.168.1.10
# 192.168.1.11
# 192.168.1.20
# 複数キーをタブ区切りで並べて出力(後続のawkやcutで処理しやすい)
jq -r '.[] | [.host, .ip] | @tsv' servers.json
# 出力:
# web01 192.168.1.10
# web02 192.168.1.11
# db01 192.168.1.20
抽出したIDやホスト名の一覧を並列処理したい場合は、 xargsコマンド解説記事 を活用して jq -r '.[].host' servers.json | xargs -P 4 -I {} ping -c 1 {} のように並列ping監視へ連携するのが効果的です。
bashのwhile readループと連携して配列を1行ずつ安全に処理する実践スクリプト
配列内の各オブジェクトに対して、Bashスクリプトで何らかのコマンド(API送信やファイルバックアップ)を1件ずつ安全に実行したい場合、 -c (compact output)と while IFS= read -r line の組み合わせ が最も堅牢な鉄板パターンです。
#!/usr/bin/env bash
set -euo pipefail
# servers.json から1行1オブジェクト形式(-c)で出力し、whileループへ渡す
jq -c '.[]' servers.json | while IFS= read -r item; do
# 1行のJSONオブジェクトから必要なフィールドを抽出
SERVER_ID=$(echo "$item" | jq -r '.id')
SERVER_HOST=$(echo "$item" | jq -r '.host')
SERVER_IP=$(echo "$item" | jq -r '.ip')
echo "[処理中] ID: ${SERVER_ID} | Host: ${SERVER_HOST} (${SERVER_IP})"
# ここにSSH疎通確認やバックアップ実行などのコマンドを記述
# ssh -o ConnectTimeout=5 "admin@${SERVER_IP}" "uptime" || true
done
改行を含まない1行JSONに圧縮して渡すため、JSON内部に改行やスペースが含まれていても誤って行分割される事故が一切起きません。安全なループ処理の設計原理については while文とreadコマンドによる安全なテキスト処理ガイド で詳しく解説しています。
実務で差がつく応用フィルターテクニック
基本的な抽出に加えて、条件分岐や配列変換、CSVエクスポートといった 現場で重宝する応用テクニック を解説します。
select() による条件付きフィルタリング(特定ステータスや数値比較)
select(条件式) は、与えられた条件が true になる要素のみを通過させる最も強力なフィルタです。SQLの WHERE 句のように扱えます。
# ログ・ステータスデータ
cat < tasks.json
[
{"id": 1, "task": "backup", "status": "success", "duration_sec": 45},
{"id": 2, "task": "sync", "status": "failed", "duration_sec": 120},
{"id": 3, "task": "cleanup", "status": "success", "duration_sec": 310},
{"id": 4, "task": "deploy", "status": "failed", "duration_sec": 80}
]
EOF
# ステータスが failed のタスクのみ抽出
jq -c '.[] | select(.status == "failed")' tasks.json
# 出力:
# {"id":2,"task":"sync","status":"failed","duration_sec":120}
# {"id":4,"task":"deploy","status":"failed","duration_sec":80}
# 複数条件:ステータスが success かつ 実行時間が 60秒超えのタスク名
jq -r '.[] | select(.status == "success" and .duration_sec > 60) | .task' tasks.json
# 出力: cleanup
# 文字列の前方一致(正規表現 test() 関数との組み合わせ)
jq -r '.[] | select(.task | test("^b|^s")) | .task' tasks.json
# 出力: backup, sync
map() による配列要素の一括変換・加工
配列の構造を崩さず、全要素に対して一括変換を施したい場合は map(フィルタ) を使います。 .[] | フィルタ だとストリームに分解されますが、 map() は **結果を再び配列の形にまとめて出力** してくれます。
# 各タスクの秒数を分に換算した新配列を作成
jq 'map({task_name: .task, duration_min: (.duration_sec / 60 | floor)})' tasks.json
# map内でselectを併用して配列のまま絞り込む
jq 'map(select(.status == "failed"))' tasks.json
JSONからCSV・TSV形式へフラット化して出力する(@csv / @tsv 構文)
JSONデータをレポート作成やExcel、スプレッドシート用にCSV出力したい場合、 @csv フィルタを使うと自動的に適切なカンマ区切りとダブルクォートエスケープを行ってくれます。
# ヘッダーなしのCSV出力
jq -r '.[] | [.id, .task, .status, .duration_sec] | @csv' tasks.json
# 出力:
# 1,"backup","success",45
# 2,"sync","failed",120
# 3,"cleanup","success",310
# 4,"deploy","failed",80
# ヘッダー行を先頭に付与した完全なCSVファイルを生成
jq -r '["Task ID", "Task Name", "Status", "Duration(s)"], (.[] | [.id, .task, .status, .duration_sec]) | @csv' tasks.json > report.csv
生成したCSVデータをBash側でパース・集計するテクニックについては、 BashでCSVを扱う方法まとめ|読み込み・ループ・配列 にて体系的に解説しています。
複数オブジェクトの結合・マージ(+ 演算子 / add)
設定ファイルのデフォルト値と個別環境設定をマージしたい場合、 + 演算子を使うとオブジェクトのフィールドが結合され、重複するキーは右側の値で上書きされます。
# デフォルト設定と本番用設定のマージ例
echo '{"timeout": 30, "debug": true, "retries": 3}' > defaults.json
echo '{"debug": false, "env": "production"}' > prod.json
# 2つのJSONをマージ(重複キーdebugはfalseで上書きされる)
jq -s '.[0] * .[1]' defaults.json prod.json
# ※ '*' はディープマージ、'+' はシャローマージ(浅い上書き)
# 複数のJSON配列を一括合体(addフィルタ)
echo '[{"a": 1}]' > part1.json
echo '[{"b": 2}]' > part2.json
jq -s 'add' part1.json part2.json
# 出力: [{"a": 1}, {"b": 2}]
curlコマンドと連携したWeb APIレスポンス自動処理実例
jq コマンドが真価を発揮するのは、 curlコマンド と組み合わせて **Web APIのレスポンスを自動処理する現場** です。API連携でよくある実践シナリオを解説します。
GitHub APIから最新リリースタグを取得するワンライナー
CI/CDスクリプトやミドルウェアの自動アップデートで頻出する、GitHubリポジトリの最新リリースタグ名を取得するワンライナーです。
# GitHub APIから最新バージョン(tag_name)を1秒で取得
LATEST_TAG=$(curl -sL https://api.github.com/repos/jqlang/jq/releases/latest | jq -r '.tag_name')
echo "jqの最新バージョン: ${LATEST_TAG}"
# 出力例: jq-1.7.1
HTTPレスポンスヘッダーとJSONボディのエラーハンドリング手順
API呼び出し時にサーバーエラー(500)や認証失敗(401/403)が発生した場合、HTMLエラーページやエラーJSONが返ってきます。これを jq にそのまま渡すと構文エラーや意図しない空値が発生します。 curl -w でHTTPステータスコードを分離検証する堅牢なスクリプト設計を紹介します。
#!/usr/bin/env bash
set -euo pipefail
API_URL="https://httpbin.org/status/200"
# レスポンスボディとHTTPステータスコードを同時に取得
RESPONSE=$(curl -s -w "
%{http_code}" "$API_URL")
# 改行で分割:末尾1行がステータスコード、残りがボディ
HTTP_STATUS=$(echo "$RESPONSE" | tail -n 1)
HTTP_BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "[エラー] HTTPステータスが不正です: ${HTTP_STATUS}" >&2
echo "エラー詳細: ${HTTP_BODY}" >&2
exit 1
fi
# jq -e でJSONの妥当性およびエラーキーの有無を検証
if echo "$HTTP_BODY" | jq -e 'has("error")' >/dev/null 2>&1; then
ERR_MSG=$(echo "$HTTP_BODY" | jq -r '.error')
echo "[APIエラー] レスポンス内にエラーが含まれています: ${ERR_MSG}" >&2
exit 1
fi
echo "[成功] 正常なAPIレスポンスを受信しました。"
安全にシェル変数をjqへ渡す「–arg」「–argjson」の正しい書き方
初心者が最もやってしまいがちな重大なアンチパターンが、 ダブルクォートでjqフィルタを囲み、シェル変数をインライン展開させる書き方 です。
# 危険なアンチパターン(コマンドインジェクションや構文エラーの原因)
USER_INPUT="Alice"
# ダブルクォートで囲むと変数内に記号やクォートが含まれた際に構文崩壊する
jq ".[] | select(.name == "${USER_INPUT}")" users.json # 厳禁!
# 安全な推奨記法:--arg を使用する
jq --arg name "$USER_INPUT" '.[] | select(.name == $name)' users.json
# 数値やブール値、配列をJSON値として渡す場合は --argjson を使用する
LIMIT=10
jq --argjson max "$LIMIT" '.[] | select(.count <= $max)' data.json
--arg を使うと、変数の値が **自動的にエスケープされた文字列** として $name にバインドされるため、シェルインジェクションや特殊文字による誤動作を100%防止できます。
よくあるエラーと落とし穴・解決策
現場で jq を使っていると遭遇しやすい代表的なエラーと、その即効解決策をまとめました。
「Cannot index array with string」エラーの原因と対策
最も頻出するエラーが jq: error (at ): Cannot index array with string "name" です。これは、 **ルートが配列([…])になっているデータに対して、直接オブジェクトのキー(.name)を指定した場合** に発生します。
# 配列データ
echo '[{"name": "Alice"}, {"name": "Bob"}]' > list.json
# エラーになる例:配列に対して直接 .name を指定している
jq '.name' list.json
# jq: error (at :1): Cannot index array with string "name"
# 正しい解決策1:配列要素を展開してからキーを指定
jq -r '.[].name' list.json
# 正しい解決策2:先頭要素のキーを取得
jq -r '.[0].name' list.json
データ構造が配列かオブジェクトか不明な場合は、まず jq 'type' data.json を実行して "array" なのか "object" なのかを確認するのが近道です。
null値やキーが存在しない場合のデフォルト値設定(// 演算子)
存在しないキーにアクセスすると、 jq はエラーを出さずに null を返します。しかしシェルスクリプトでは、値が存在しない場合にデフォルト値(フォールバック)を割り当てたいケースが多々あります。これには // (オルタナティブ演算子) を使用します。
# nicknameが存在しないユーザーデータ
echo '{"name": "Charlie"}' > user.json
# 通常は null が返る
jq -r '.nickname' user.json
# 出力: null
# // 演算子でフォールバック値を指定
jq -r '.nickname // .name // "名無し"' user.json
# 出力: Charlie(nicknameがないためnameを採用)
# キーが存在しない場合の固定デフォルト値
jq -r '.role // "guest"' user.json
# 出力: guest
まとめ
Linux環境におけるJSONデータ処理の必須ツール jq の使い方を解説しました。実務で即戦力となる要点をまとめます。
- シェル変数への代入は
-rが鉄則: ダブルクォートを排除した生テキストを出力し、後続処理でのエスケープ事故を防ぐ。 - 配列のループ処理は
-cとwhile read: 1行1JSONに圧縮して渡すことで、改行やスペースによる行分割バグを完全に排除する。 - 変数の受け渡しは
--arg/--argjson: インライン展開を避け、インジェクションと構文エラーを防止する安全設計を徹底する。
関連記事(内部リンク)
- curlコマンドの使い方完全ガイド:Web API連携やHTTPリクエスト送信の基本を網羅
- bashのwhile文とreadコマンド完全ガイド:テキストファイルやJSON行を安全に1行ずつ処理するスクリプト設計
- bashでCSVを扱う方法まとめ:JSONから変換したCSVの読み込み・ループ・配列処理の実務パターン
- xargsコマンドの使い方早見表:jqで抽出したIDやホスト名の一括並列実行(-P)テクニック

コメント