配列っぽいデータが含まれる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_acdone