{ “research”: [ “POSIX awk specifications”, “JSON streaming parsing techniques (SAX)”, “systemd timer/service units”, “Safe shell scripting (set -euo pipefail, trap)” ], “plan”: [ “Implement a character-based or delimiter-based tokenizer in POSIX awk”, “Design a robust Bash wrapper with error handling”, “Provide a systemd example for automation”, “Ensure portability and idempotency” ], “style_applied”: “SRE/DevOps professional tone, structured metadata, clear Mermaid diagrams” } 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
超巨大JSONを最小メモリで走査する:POSIX awkによるSAX型ストリーミング解析の自動化
【導入と前提】
ギガバイト単位のJSONログを、システムメモリを圧迫せずに高速抽出・フィルタリングするストリーム処理基盤を構築します。
OS/実行環境: Linux (Debian/RHEL系)
必須ツール: POSIX準拠
awk(mawk/gawk),bash4.0+,curl,jq(バリデーション用)
【処理フローと設計】
データの全体をメモリにロードせず、1文字または1トークンずつ読み進めるSAX(Simple API for XML)ライクなアプローチを採用します。
graph TD A["JSON Data Source/API"] -->|curl --silent| B["Stream Tokenizer"] B -->|Token Pipeline| C["POSIX awk SAX Logic"] C -->|Event: Match Key/Value| D["Data Extraction"] D -->|Output| E["Local Log / Dashboard"] C -->|Error State| F[Trap/Alert]
awk 内部では、現在のネスト深度(Depth)とパスをスタックとして管理し、特定のキーが出現した際のみアクションをトリガーします。
【実装:堅牢な自動化スクリプト】
#!/bin/bash
# stream_json_parser.sh - Memory-efficient JSON streaming parser
set -euo pipefail
trap 'echo "[ERROR] Line $LINENO: Command failed with exit code $?" >&2' ERR
# --- 設定項目 ---
API_URL="${1:-'http://localhost:8080/large-data.json'}"
TEMP_DIR="/tmp/json_parser"
mkdir -p "$TEMP_DIR"
# --- POSIX awk によるSAX風パーサー本体 ---
# このスクリプトはJSONをトークン化し、特定のパス(例: .items[].id)を抽出する
parse_json_stream() {
awk -v target_key="id" '
BEGIN {
FS = "\"" # 文字列セパレータとしてダブルクォートを使用
depth = 0
}
{
# シンプルなトークナイズ(簡略化版)
# 実際の実装では文字列内のエスケープ処理等を考慮する必要がある
for (i = 1; i <= NF; i++) {
token = $i
if (token ~ /\{/) { depth++; }
if (token ~ /\}/) { depth--; }
# 前のフィールドがキー名で、現在が値であるか判定
# 例: "id": "123" -> $i-1 が id
if ($(i-1) == target_key) {
# 次のフィールド(またはセパレータ後の値)を出力
# セパレータ ":" を飛ばして値を取得
sub(/^[[:space:]]*:[[:space:]]*/, "", $i)
print $i
}
}
}'
}
# --- メイン処理 ---
main() {
echo "[INFO] Starting stream parsing from: $API_URL"
# curlのオプション解説:
# -f: HTTPエラー時に失敗させる
# -L: リダイレクトを追跡
# -s: 進捗表示を非表示
# -S: エラー時はメッセージを表示
curl -fLSs "$API_URL" | \
jq -c . | \
parse_json_stream
echo "[INFO] Stream processing completed successfully."
}
main
systemd ユニットファイル設定例
定期的なログ収集と解析を自動化する場合のタイマー設定です。
# /etc/systemd/system/json-parser.service [Unit] Description=Stream JSON Parser Service After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/stream_json_parser.sh "https://api.example.com/metrics" User=parser-user Group=parser-user # /etc/systemd/system/json-parser.timer [Unit] Description=Run JSON Parser every 5 minutes [Timer] OnCalendar=*:0/5 Persistent=true [Install] WantedBy=timers.target
【検証と運用】
正常系の確認:
# テスト用JSONを生成してパイプ渡し echo '{"status":"ok", "items":[{"id":"A1"}, {"id":"B2"}]}' | ./stream_json_parser.sh /dev/stdinログの確認:
systemd経由で実行している場合はjournalctlを使用します。journalctl -u json-parser.service -f
【トラブルシューティングと落とし穴】
権限問題:
/tmp/json_parserなどの書き込み権限を確認してください。systemdで実行する場合はProtectSystem=full等のセキュリティ設定との整合性に注意が必要です。環境変数の保護: APIキーなどを含む場合は、スクリプト内にハードコードせず
EnvironmentFile=を使用してsystemdから読み込んでください。JSONのエスケープ: POSIX
awkで複雑なJSON(値の中にエスケープされた"がある場合など)を処理するのは限界があります。その場合はjq -cで一行形式に整形してから処理する、あるいはsedでトークナイズの前処理を行うのが定石です。
【まとめ】
運用の冪等性を維持するための3つのポイント:
ステートレスな設計: 処理の成否をファイルの状態に依存させず、パイプラインの終了ステータスで判断する。
リソース制限の強制: 巨大JSONを扱う際は
curl --max-timeやawkのメモリ制限を意識し、システム全体の死滅(OOM killer)を防ぐ。クリーンアップの徹底:
trapコマンドにより、異常終了時でも一時ファイルや残存プロセスを確実に消去する。

