jq コマンドで存在しないキーを指定して値を取得しようとした場合に null という文字列が取得されてしまうことへの対処

Bash

はじめに

AWS、GCP といったクラウドサービスや、
SaaS サービスの利用が進むにつれて、 外部 API を呼び出してレスポンスされた JSON を処理するケースが多くなってきています。

JSON を処理するためのコマンドラインツールといえば jq コマンドがデファクトスタンダードかと思います。

そんな jq コマンドをつかってシェルスクリプトを作成していたときのことです。

JSON データから jq コマンドで必要な属性だけを取得しましたが、たまに JSON データに想定した情報が存在せず、 jq 実行結果が null となっていまい、
後続処理で null という文字を使って処理を行う用になってしまっていました。

これをなんとかしたというのが今回の主旨です。

(説明がわかりにくいですね)

具体例をあげながら対処方法を記載していきます。

検証環境

$ uname -moi
arm64 MacBookPro18,3 Darwin

$ bash -version | head -n 1
GNU bash, バージョン 5.1.16(1)-release (aarch64-apple-darwin21.1.0)

問題

まずはかんたんな JSON を echoで出力してみます。

$ echo '{"x":"hello"}'
{"x":"hello"}

jq を使って、上記の出力結果から x の値だけを抽出してみます。

$ echo '{"x":"hello"}' | jq .x
"hello"

抽出できましたが、ダブルクォートが不要なのでこれを出力しないようにします。

jq コマンドに -r オプションを付与するだけです。

$ echo '{"x":"hello"}' | jq -r .x
hello

さて、ここからです。
jq コマンドをパイプで繋いだ前方のコマンドが常に想定されるフォーマットを返すわけではないとします。
外部の API を呼び出す場合には、想定したフィールド(ここでは x)が常に存在しているわけではないでしょう。

$ echo '{"y":"world"}' | jq -r .x
null

存在しない属性を jq コマンドで参照した場合、 null という文言が出力されてしまいます。

これを変数に代入していたりした場合は更に話が面倒です。

$ GREETING=$(echo '{"y":"world"}' | jq -r .x)

$ echo $GREETING
null

jq を実行した結果が想定通りかどうかを null という文字列かどうかで判断することもできますが、ちょっと気持ち悪いですよね。

jq--exit-status オプションを使用

jq コマンドに用意されている --exit-status オプションを利用することで対応しました。

# 属性xが存在している場合
$ GREETING=$(echo '{"x":"hello"}' | jq --exit-status -r .x) || echo "Failed"

$ echo $GREETING
$ hello

# 属性xが存在していない場合
$ GREETING=$(echo '{"y":"world"}' | jq --exit-status -r .x) || echo "Failed"
$ Failed

$ echo $GREETING
$ null

存在していない属性を参照した場合、 変数に格納される値は null となりかわりません。

ただし、 jq 実行結果が失敗ステータスとなるため、 || 以降の部分が実行されます。

ここでエラーハンドリングを行うことができるというわけです。

ひとこと

もっと良い方法ありそうですが、 今回はこれで事足りました。

Bash