配列っぽいデータが含まれるJSONデータをjqコマンドとシェルを組み合わせて処理する方法.
こういう感じのJSONデータ (states.json) があるとして.
{
"states": [
{
"name": "北海道",
"name_ac": "ほっかいどう"
},
{
"name": "青森",
"name_ac": "あおもり"
},
{
"name": "岩手",
"name_ac": "いわて"
},
{
"name": "宮城",
"name_ac": "みやぎ"
},
{
"name": "秋田",
"name_ac": "あきた"
},
{
"name": "山形",
"name_ac": "やまがた"
},
{
"name": "福島",
"name_ac": "ふくしま"
}
]
}
こんな感じで出力させるものとする.
北海道 ほっかいどう
青森 あおもり
岩手 いわて
宮城 みやぎ
秋田 あきた
山形 やまがた
福島 ふくしま
整形して出力
単にJSONを見やすく整形出力したいなら, これだけ.
jq '.' < states.json
JSONのなかの特定のプロパティにアクセスする
プロパティ名を指定すればよい.
jq '.states' < states.json
次の結果が得られる.
[
{
"name": "北海道",
"name_ac": "ほっかいどう"
},
{
"name": "青森",
"name_ac": "あおもり"
},
{
"name": "岩手",
"name_ac": "いわて"
},
{
"name": "宮城",
"name_ac": "みやぎ"
},
{
"name": "秋田",
"name_ac": "あきた"
},
{
"name": "山形",
"name_ac": "やまがた"
},
{
"name": "福島",
"name_ac": "ふくしま"
}
]
シーケンシャルに処理できるようにする
配列などのデータを扱うなら, for/while で処理できたほうがいい.
jqの機能 length と seqコマンドを組み合わせる例を見かけることがあるけど, 個人的にはwhileを好む.
jq -c '.states[]' < states.json
-c
オプションで, 出力されるJSONを1行にできる.
フィルタ (ここまであげた例でいうシングルクォートでくくってる箇所) で指定してるプロパティに []
をつけると,
1アイテムを1行ずつ出力してくれる.
{"name":"北海道","name_ac":"ほっかいどう"}
{"name":"青森","name_ac":"あおもり"}
{"name":"岩手","name_ac":"いわて"}
{"name":"宮城","name_ac":"みやぎ"}
{"name":"秋田","name_ac":"あきた"}
{"name":"山形","name_ac":"やまがた"}
{"name":"福島","name_ac":"ふくしま"}
whileループで処理する
ここまでできると, パイプと while read
とさらにjqコマンドを組み合わせることで,
各アイテムごとのプロパティにアクセスできるようになる.
jq -c '.states[]' < states.json | \
while read state; do
jq -r '[.name, .name_ac] | @tsv' <<< $state | (
read name name_ac
echo $name $name_ac
)
done
-r
オプションでダブルクォートでくくるのをなしにしている.
フィルタでは [.name, .name_ac]
でリストっぽい出力にし, jqの組み込み機能でtsv (タブ区切りのシーケンス) に変換し, readコマンドで変数へセットしてる.
readで各アイテムを読みだした後, 変数へセットするところは, 素直に次のように書いてもいいと思う.
(jqコマンドの呼び出しは少なくできるけど, 値にスペースが含まれてることを考えると, このほうが使いやすいかも)
jq -c '.states[]' < states.json | \
while read state; do
name=$(jq -r '.name' <<< $state)
name_ac=$(jq -r '.name_ac' <<< $state)
echo $name $name_ac
done
パイプとプロパティ名による組み合わせでも, 同等の結果を得られる (1/6 追記)
コメントいただきました.
これでも同じ結果を得られます.
$ jq -r -c '.states[] | "\(.name) \(.name_ac)"' < states.json
パイプした先でプロパティ名でアクセスする感じすな.
こっちのほうがかんたんだー.