JSONを処理したい(jq)

$ jq [オプション] <フィルタ> [ファイル名...]
$ cat ファイル名.json | jq <フィルタ>

jqは、JSON形式のファイルを整形して読みやすく表示するコマンドです。 JSON形式は、JavaScript Object Notationの略で、データを構造化して表現するためのフォーマットです。 コンピューター間のデータのやり取りや、設定ファイルのフォーマットとして広く使われていますが、人間が直接読むには少し難しい形式です。

インストールしたい(jq)

$ brew install jq
$ jq --version
jq-1.8.2

jqコマンドでJSON形式のファイルを確認できます。

すべて表示したい(.)

$ jq . ファイル名.json
{
  "name": "test",
  "version": "1.0.0",
  "fruits": [
    "apple",
    "banana",
    "cherry"
  ],
  "config": {
    "debug": true,
    "count": 3
  }
}

.フィルターで、JSONファイルの内容をすべて表示できます。

特定のキーの値を表示したい(.key)

$ jq '.name' ファイル名.json
"test"

$ jq '.config.debug' ファイル名.json
true

.keyフィルターで、指定したキーの値だけを取り出せます。 .でつなげると、ネストしたオブジェクトの値も取り出せます。

注釈

jqのフィルターは、大きく分けて2種類の部品からできています。

  • パスアクセス:.、.key、.[0]など、JSONの中の値を指し示す記法

  • 組み込み関数:keys、length、select(...)、map(...)など、値に処理を加える関数

この2つは|(パイプ)でいくつでも繋げられます(例:.name | length)。 先頭にドットが付くかどうかで役割が変わるので、.keys(keysという名前のキーへのアクセス、多くの場合nullになる)とkeys(オブジェクトのキー一覧を返す関数)を混同しないように注意してください。

文字列をダブルクォートなしで表示したい(-r)

$ jq '.name' ファイル名.json
"test"

$ jq -r '.name' ファイル名.json
test

-r(--raw-output)オプションで、文字列の結果からダブルクォートを外して表示できます。 シェルスクリプトで値をそのまま使いたいときに便利です。

// 意図しない結果
$ NAME=$(jq '.name' ファイル名.json)
$ echo "${NAME}.txt"
"test".txt

// 期待する結果
$ NAME=$(jq -r '.name' ファイル名.json)
$ echo "${NAME}.txt"
test.txt

デフォルト(-rなし)の結果をシェル変数に代入すると、ダブルクォートまで含めて代入されてしまいます。 ファイル名の組み立てやif [ "$NAME" = "test" ]のような文字列比較で、意図しない不一致やおかしなファイル名の原因になるので注意してください。

配列の要素を展開したい([])

$ jq '.fruits' ファイル名.json
["apple", "banana", "cherry"]

$ jq '.fruits[]' ファイル名.json
"apple"
"banana"
"cherry"

.fruitsは配列をひとつの値としてそのまま返しますが、.fruits[]のように[]をつけると配列の中身を1個ずつバラして返します。 後続のフィルターを配列の各要素に適用したい場合は、.fruits[]のように展開しておく必要があります。

最上位のオブジェクトを表示したい(keys)

$ jq 'keys' ファイル名.json
[
  "config",
  "fruits",
  "name",
  "version"
]

keysフィルターで最上位のオブジェクトを抽出できます。 大きなJSONファイルの概要を把握するのに使います。

$ jq '.[0] | keys' ファイル名.json

最上位のセクションが配列の場合、最初の要素(.[0])に対してキーを取り出すようにします。

配列を絞り込みたい(select)

$ jq '.fruits[] | select(. == "banana")' ファイル名.json
"banana"

.fruits[]で配列の要素を1つずつ取り出し、select(条件式)で条件に合う要素だけに絞り込めます。 オブジェクトの配列から特定の条件を満たすものだけ抜き出したいときにも使います。

配列の値を変換したい(map)

$ jq '.fruits | map(. + "!")' ファイル名.json
[
  "apple!",
  "banana!",
  "cherry!"
]

map(フィルター)で、配列の各要素に同じ処理を適用できます。

1行にまとめて表示したい(-c)

$ jq -c '.' ファイル名.json
{"name":"test","version":"1.0.0","fruits":["apple","banana","cherry"],"config":{"debug":true,"count":3}}

-c(--compact-output)オプションで、改行やインデントなしの1行で出力できます。 ログとして記録したり、次のコマンドにパイプで渡したりするときに便利です。

キーの順番を揃えたい(-S)

$ jq -S '.' ファイル名.json
{
  "config": {
    "count": 3,
    "debug": true
  },
  "fruits": [
    "apple",
    "banana",
    "cherry"
  ],
  "name": "test",
  "version": "1.0.0"
}

-S(--sort-keys)オプションで、オブジェクトのキーをアルファベット順に並べ替えて表示できます。 JSONファイルの差分を比較したいときなど、キーの並び順を揃えておくと見やすくなります。

任意の階層のオブジェクトを表示したい

$ jq -r 'paths(scalars) | select(length == N) | [.-1]' ファイル名.json

paths(型)フィルターで、型 にマッチしたオブジェクトのすべてのパスの配列形式のリストを取得できます。 scalarsはスカラー型のフィルターで数値、文字列、true / false / nullにマッチします。 型を指定しない場合は「すべて(scalars / objects / arrays / numbers / strings / booleans / nulls)」にマッチします。 select(条件式)フィルターで、条件式にマッチした値を取得できます。 .[-1]フィルターで、リストの最後の要素を取得できます。

親の階層も表示したい

$ jq -r 'paths(scalars) | select(length == 3) | "\\(.[1]) > \\(.[2])"' ファイル名.json
Level2_Key1 > Level3_Key1
Level2_Key2 > Level3_Key2
Level2_Key3 > Level3_Key3

\(...)の文字列テンプレート機能で、表示する文字列を制御できます。 .[1]でパスのインデックスが1(=2番目)の要素、 .[2]でパスのインデックスが2(=3番目)の要素が取得できるので レベル2 > レベル3のようにキーが表示されます。

$ jq -r 'paths(scalars) | select(length == 3) | "\\(.[1]) > \\(.[2])"' ファイル名.json | sort | uniq

大きなJSONファイルだと、同じ構造が繰り返されていることが多いです。 sortコマンドとuniqコマンドと組み合わせると、重複を削除できます。 キーの順番は失われますが、ファイルの概要を把握するのに役立ちます。

構造をしりたい

$ jq 'paths | length' ファイル名.json | sort -n | tail -1

ファイルの階層の深さを確認できます。 pathsフィルターで、すべてのオブジェクトをリストに変換します。 lengthフィルターで、リストのサイズを取得します。 sort -nで番号順にソートし、tail -1で末尾の値を表示します。

$ jq 'walk(if type == "object" or type == "array" then . else type end)' ファイル名

ファイルの階層構造を保ちながら、値の型を表示します。

YAMLしたい

jq -r 'paths(scalars) as $p | "\($p | join(".")) = \(getpath($p))"' ファイル名.json

擬似的なYAML形式で表示します。

シェル変数を渡したい(--arg)

$ NAME="test"
$ jq --arg n "$NAME" '.name == $n' ファイル名.json
true

--arg 変数名 値で、シェル変数をjqのフィルター内で$変数名として使えます。 検索したい値をシェルスクリプトから動的に渡したいときに使います。

シェルスクリプトで条件分岐したい(-e)

$ jq -e '.name == "test"' ファイル名.json
true

$ echo $?
0

-e(--exit-status)オプションで、結果がfalseまたはnullのときに終了コード1、それ以外は0を返すようになります。 if jq -e ... ; thenのように、シェルスクリプトの条件分岐に組み込めます。