深夜2時。Telegramにアラートが届いた:サービスのヘルスチェックが連続して失敗している。サーバーにSSHすると、tail -fのログが1行に延々と続く巨大なJSONを吐き出している。statusフィールドが何を示しているのか、クラスター内のどのノードがエラーになっているのかをすぐに確認しなければならなかった。
そのとき選択肢は3つあった:生のテキストを検索するgrep、JSONをパースするためにPythonを起動する、あるいはjqを使う。それが、jqがなぜ生まれたのかを初めて本当に理解した瞬間だった。
TerminalでJSONを処理する3つの方法を比較
同じ問題設定:APIがJSON形式でレスポンスを返し、statusフィールドを抽出する必要がある:
curl -s https://api.example.com/health
# 出力: {"id":1,"status":"error","message":"Connection timeout","node":"web-03"}
方法1:grepとsed
curl -s https://api.example.com/health | grep -o '"status":"[^"]*"'
# "status":"error"
メリット:すべてのLinuxに標準搭載されており、追加インストール不要。
デメリット:結果は"status":"error"のままで、純粋な値ではない。サーバーが"status" : "error"のようにスペースを含んで返したり、フィールドの順序が変わったりすると、正規表現が誤検知するか何もマッチしなくなる。本番環境で深夜2時に「運任せ」は良い選択肢ではない。
方法2:Pythonのワンライナー
curl -s https://api.example.com/health | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['status'])"
# error
メリット:JSONを正しくパースするため、フォーマットやフィールドの順序に左右されない。
デメリット:頭が完全に覚醒していない状態でタイプするには長くて難しいコマンドだ。ネストされたオブジェクトや配列にアクセスする必要があるとき、コマンドはさらに複雑になる。
方法3:jq
curl -s https://api.example.com/health | jq '.status'
# "error"
1つのコマンド。短い。明確。結果はすぐに出る。
分析:どのツールをいつ使うべきか
- grep/sed:サーバーへの追加ツールインストールが許可されず、JSON構造が非常にシンプルでフォーマットが絶対に変わらない場合のみ。
- Python:複雑なロジック、多段階の条件処理、またはデータを変換してファイルに書き出す場合——jqでも可能だが、長いスクリプトではPythonの方がメンテナンスしやすい。
- jq:TerminalやShellスクリプトでJSONを素早くフィルタリング・抽出・変換するための定番ツール。これがデフォルトの選択肢だ。
10台以上のVPSを3年間管理してきた中で、こういう経験が一度あった:APIベンダーがレスポンスのフォーマットを変更し、grepは何も捕捉できずに静かに失敗し、スクリプトは普通に動いているように見えた——ただし読み取れたstatusは空文字列だった。jqにはそういう問題がない。標準に従って正しくパースするため、フォーマットやフィールドの順序に依存しない。
jqのインストール
# Ubuntu/Debian
sudo apt install jq
# CentOS/AlmaLinux/RHEL
sudo dnf install jq
# Arch Linux
sudo pacman -S jq
# バージョン確認
jq --version
# jq-1.7.1
jqの基本構文
JSONを見やすくフォーマットする
デバッグの第一歩:JSONを整形して全体を表示する:
curl -s https://api.example.com/health | jq '.'
.(ドット)は「入力全体を取得して整形する」という意味だ。読めない長い1行の代わりに、次のように表示される:
{
"id": 1,
"status": "error",
"message": "Connection timeout",
"node": "web-03"
}
特定のフィールドを取得する
# フィールドを1つ取得
echo '{"status":"error","node":"web-03"}' | jq '.status'
# "error"
# 複数のフィールドを同時に取得
jq '.status, .node'
# ネストされたフィールド
jq '.server.config.port'
配列を操作する
cat nodes.json
# [{"name":"web-01","status":"ok"},{"name":"web-02","status":"ok"},{"name":"web-03","status":"error"}]
# すべての要素
cat nodes.json | jq '.[]'
# 最初の要素(インデックス0)
cat nodes.json | jq '.[0]'
# 各要素のnameフィールドを取得
cat nodes.json | jq '.[].name'
# "web-01"
# "web-02"
# "web-03"
select()でフィルタリング——デバッグで最もよく使う機能
リスト全体を読む代わりに、エラーになっているノードだけを正確に抽出する:
cat nodes.json | jq '.[] | select(.status == "error")'
# {
# "name": "web-03",
# "status": "error"
# }
# エラー中のノード名だけを取得
cat nodes.json | jq -r '.[] | select(.status == "error") | .name'
# web-03
DockerとKubernetesでの実践的な活用
jqは外部APIだけで使うものではない。DockerもKubernetesもJSONで出力する——そこでjqが真価を発揮する:
# bridgeネットワーク内のすべてのコンテナ名とIPを表示
docker network inspect bridge | jq '.[0].Containers | to_entries[] | {name: .value.Name, ip: .value.IPv4Address}'
# nginxコンテナのポートマッピングを表示
docker inspect nginx | jq '.[0].NetworkSettings.Ports'
# appコンテナからデータベースの環境変数を取得
docker inspect myapp | jq -r '.[0].Config.Env[]' | grep DB_
# Running状態でないKubernetesのPodを検索
kubectl get pods -o json | jq -r '.items[] | select(.status.phase != "Running") | .metadata.name'
データの変換——レスポンスから新しいオブジェクトを作成
# ノードリストから簡潔なレポートを作成
curl -s https://api.example.com/nodes | jq '[.[] | {node: .name, healthy: (.status == "ok")}]'
# [
# {"node": "web-01", "healthy": true},
# {"node": "web-03", "healthy": false}
# ]
# エラー中のノード数をカウント
cat nodes.json | jq '[.[] | select(.status == "error")] | length'
# 1
jqの結果をシェル変数に使う
# デフォルトではjqは文字列をダブルクォートで囲む
echo '{"node":"web-03"}' | jq '.node'
# "web-03"
# -rフラグでクォートを除去——変数に直接代入できる
NODE=$(curl -s https://api.example.com/health | jq -r '.node')
ssh admin@$NODE 'sudo systemctl restart myapp'
サーバーにいないときのJSON確認
jqフィルターを書く前にJSONレスポンスの構造を確認したいとき——でもローカルマシンにいて、まだサーバーにSSHしていない。そういうときはToolCraftのJSON Formatterをよく使う:JSONをペーストすれば即座にフォーマットされ、シンタックスハイライトが明確で、構文エラーもすぐに検出してくれる。ブラウザ上で完全に動作し、データはどこにも送信されない——APIレスポンスにトークンや機密情報が含まれている場合に重要だ。
KubernetesやAnsibleをよく使うなら、同じサイトのYAML ↔ JSON Converterも非常に便利だ——YAMLマニフェストをJSONに変換してjqフィルターを書いてテストし、必要に応じて元に戻せる。
よく使うjqチートシート
jq '.' # 整形して表示
jq '.field' # フィールドを取得
jq '.a.b.c' # ネストされたフィールド
jq '.[]' # すべての配列要素
jq '.[0]' # 最初の要素(インデックス0)
jq '.[] | select(.x == "val")' # 文字列でフィルタリング
jq '.[] | select(.n > 10)' # 数値でフィルタリング
jq '{new_key: .old_key}' # 新しいオブジェクトを作成
jq '[.[] | .name]' # フィールドから配列を作成
jq 'length' # 要素数をカウント
jq 'keys' # オブジェクトのキー一覧
jq 'unique' # 重複を除去
jq 'sort_by(.field)' # フィールドでソート
jq -r '.field' # 生の出力(ダブルクォートを除去)
jq -c '.' # コンパクト出力(1行)
あの深夜2時のデバッグ以来、新しいサーバーをセットアップするとき最初にインストールするのがjqだ。セットアップのRunbookにsudo apt install jqを追加するだけでいい——次にログで大量のJSONに遭遇しても、ブラウザにコピーしなければ読めないという状況はなくなる。

