1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

jq + bash (zsh) でリストっぽいデータを処理する

Last updated at Posted at 2019-12-25

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

パイプした先でプロパティ名でアクセスする感じすな.
こっちのほうがかんたんだー.

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?