--- METADATA: SERVICE: jq-data-extraction CONTEXT: SRE/DevOps Automation TARGET: Robust JSON Parsing AUTHOR: AI Assistant (Gemini) DATE: 2024-07-29 VERSION: 1.0 ---
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
jqによるJSONデータからの複数フィールド抽出とTSV/CSV整形
【導入と前提】
外部APIや設定ファイルから取得したJSONデータについて、複数の重要な属性値(ID, ステータス, タイムスタンプなど)を効率的かつ堅牢に取り出し、標準的な区切り形式(TSV/CSV)に整形する自動化スクリプトを作成します。
実行環境の前提条件:
| 項目 | バージョン/備考 |
|---|---|
| OS | GNU/Linux (Bash 4.x以上) |
| JSONパーサー | jq 1.6+ |
| HTTPクライアント | curl |
【処理フローと設計】
このスクリプトは、APIからJSONを取得し、jqを用いて必要な属性値を指定された区切り形式で抽出・整形し、後続のシェル処理で利用可能にするプロセスを設計します。
graph TD
A["外部APIからのJSON取得 (curl)"] --> B{"jqフィルタ適用"}
B -->|複数キー選択| C["文字列補間によるTSV整形"]
C -->| -r オプション適用| D["生文字列出力 (ダブルクォート除去)"]
D --> E["シェル変数への格納またはファイル出力"]
- ポイント:
jq内で複数のキーを処理し、-r(raw output) オプションで文字列のダブルクォーテーションを確実に除去することで、シェルスクリプトで扱いやすいプレーンテキスト(TSV/CSV)を生成します。
【実装:堅牢な自動化スクリプト】
以下のスクリプトは、複数レコードを持つJSON配列から、ID、名前、ステータスの3つのフィールドをタブ区切り値(TSV)として抽出する例です。
#!/bin/bash
# --- 堅牢性設定 ---
# -e: コマンドが失敗した場合、即座に終了
# -u: 未定義の変数を使用した場合、エラー
# -o pipefail: パイプライン中の任意のコマンドが失敗した場合、終了ステータスを失敗にする
set -euo pipefail
# 必要な外部コマンドのチェック
if ! command -v jq >/dev/null || ! command -v curl >/dev/null; then
echo "ERROR: jq or curl command not found. Please install them." >&2
exit 1
fi
# --- 変数定義 ---
API_ENDPOINT="https://jsonplaceholder.typicode.com/users" # テスト用公開API
OUTPUT_FILE="./processed_data_$(date +%Y%m%d%H%M%S).tsv"
TEMP_JSON="/tmp/api_response_$$.json"
# --- クリーンアップ処理 ---
# スクリプトが終了 (正常終了、中断、エラー終了に関わらず) した場合に実行される
cleanup() {
rm -f "${TEMP_JSON}"
echo "INFO: Temporary files cleaned up."
}
trap cleanup EXIT
# --- jq フィルタ定義 ---
# .[]: JSON配列をイテレート
# "\(.id)\t\(.name)\t\(.username)": 文字列補間を用いて、タブ(\t)区切りで値を出力
JQ_FILTER='
.[] |
"ID\tNAME\tUSERNAME", # ヘッダ行 (最初の一回だけ出力)
"\(.id)\t\(.name)\t\(.username)"
'
echo "INFO: Fetching data from API..."
# 1. APIからのデータ取得 (リトライ処理を含む)
# -s: サイレントモード
# -L: リダイレクトを追跡
# -f: HTTPステータスコードが400以上なら失敗とみなす
# --retry 3: 3回までリトライを試みる
if ! curl -sL -f --retry 3 "${API_ENDPOINT}" -o "${TEMP_JSON}"; then
echo "FATAL: Failed to retrieve data from ${API_ENDPOINT} after multiple retries." >&2
exit 2
fi
# 2. jqによる複数フィールド抽出と整形
# -r (raw output): jqの出力から文字列のダブルクォーテーションを除去 (必須)
# -f: フィルタをファイルではなく引数で指定
if ! jq -r "${JQ_FILTER}" "${TEMP_JSON}" > "${OUTPUT_FILE}"; then
echo "FATAL: jq processing failed." >&2
exit 3
fi
echo "SUCCESS: Data extraction completed."
echo "Output saved to: ${OUTPUT_FILE}"
cat "${OUTPUT_FILE}" | head -n 5
【検証と運用】
正常系の確認
スクリプトを実行し、期待通りTSV形式でダブルクォーテーションなしのデータが出力されているかを確認します。
# スクリプトを実行
./process_data.sh
# 出力ファイルの先頭5行を確認
# 期待される出力:
# ID NAME USERNAME
# 1 Leanne Graham Bret
# 2 Ervin Howell Antonette
# 3 Clementine Bauch Samantha
# 4 Patricia Lebsack Karianne
# jq -r が動作しているか検証
if grep '"' "${OUTPUT_FILE}"; then
echo "ERROR: Output still contains double quotes."
else
echo "Verification successful: Raw output (-r) applied correctly."
fi
エラー時のログ確認方法
自動化を systemd や cron で実行する場合、エラーメッセージは標準エラー出力 (stderr) に送られます。systemd ユニットとして運用する場合は journalctl で確認します。
# systemd サービス名が data-extractor.service の場合 journalctl -u data-extractor.service --since "1 hour ago" -r
【トラブルシューティングと落とし穴】
1. jq -r の適用漏れ
jq で文字列値を取り出す際に -r オプションを忘れると、全ての文字列がシェルにとって特別な意味を持つダブルクォーテーションで囲まれて出力されます。これは後続のシェル処理(cutやawk)で誤動作の原因となります。
- 解決策: 文字列を扱いたい出力処理には必ず
jq -rを適用すること。
2. 環境変数および機密情報の扱い
APIキーなどの機密情報をスクリプト内にハードコードせず、安全に環境変数として渡すか、HashiCorp Vaultなどの専用のシークレット管理ツールを使用します。
# 環境変数を使用する例(curlで認証ヘッダを渡す)
API_TOKEN="YOUR_SECRET_TOKEN"
# 外部APIの呼び出し部分(例)
curl -sL -H "Authorization: Bearer ${API_TOKEN}" "${API_ENDPOINT}"
3. 一時ファイルのクリーンアップの失敗
trap cleanup EXIT を設定していても、OSが強制終了した場合(OOM Killerなど)は実行されない可能性があります。今回の例では /tmp を利用していますが、機密性の高いデータを扱う場合は、一時ファイルが確実に削除されるよう、mktemp を使用し、ファイルディスクリプタとして扱う堅牢な方法を検討します。
【まとめ】
SRE/DevOpsの自動化において、JSON処理の堅牢な基盤を構築するための冪等性維持のポイントは以下の3点です。
入出力の予測可能性の確保(
jq -rの徹底):jq -rオプションを常に利用し、出力形式をシェルが扱いやすいプレーンテキスト(TSV/CSV)に統一することで、後続処理(awk,sortなど)が環境やjqのバージョンに依存せず、常に同じ結果を得られるようにします。実行環境の分離と設定の集中:
set -euo pipefailやtrapを用い、実行環境の失敗時の挙動を予測可能にします。スクリプト内でパスや環境変数が設定に依存しすぎないように(TEMP_JSONに$$を使うなど)分離します。状態変更の原子性確保: 外部APIへのデータ書き込みなど状態を変更する操作の前には、入力データのバリデーション(例:
jqによるスキーマチェック)を徹底し、中途半端な状態変化を防ぎます。 “`

