Bash で「連想配列の配列」を扱いたい…けど jq でいいか

Bash 内で、構造データを扱いたいなと思った。JSON みたいな感じで、複数の連想配列を抱えた配列を扱ったりしたいなーと。

Bash での配列は declare -a を使って、array=() みたいな感じで表せる。

また、連想配列についても declare -A で扱えるのだが、Bash v4 以降じゃないと使えない構文だった。

で、この連想配列を複数束ねた配列は作れないのかなと思って調べたが、どうも良い感じのサンプルが見つからなかった。

色々調べていたら、もっと単純な方法を見つけた。

JSON をヒアドキュメントで書いて、それを jq コマンドに食わせて扱っていた。…なんだ、それでいいのか。

というワケで、ちょっとしたサンプルを作ってみた。

#!/bin/bash

# 「連想配列の配列」を JSON 形式で書く
# 構文エラーがあると `jq` コマンド実行時にエラーになるので注意
hash_array="$(cat << EOL
  [
    { "id": 1, "name": "Foo" },
    { "id": 2, "name": "Bar" },
    { "id": 3, "name": "Baz" }
  ]
EOL
)"

# `for` 文でループを回すため、配列の最後のインデックス値を求める
array_end_index="$(echo "${hash_array}" | jq '. | length - 1')"

# 連想配列を1つずつ取得して処理する例
# `$(seq)` をダブルクォートで囲んではダメ
for index in $(seq 0 "${array_end_index}"); do
  object="$(echo "${hash_array}" | jq ".[${index}]")"
  echo "--- ${index}"
  echo "${object}"
done

…こんな感じで、jq を使いまくる。for ループ内の変数 object は、一つの連想配列を蓄えているので、後はココからお好みで idname プロパティを拾って処理したりすればよかろう。

一応、出力例は以下のとおり。

--- 0
{
  "id": 1,
  "name": "Foo"
}
--- 1
{
  "id": 2,
  "name": "Bar"
}
--- 2
{
  "id": 3,
  "name": "Baz"
}

Bash のバージョンを求めるのは環境によって手間が大きいが、こうやって jq コマンドだけ用意すれば良いというのは、まだ楽だと思う。

勿論、jq 内で Map 処理することも出来たりするが、データを元に外部コマンドを組み合わせたりする際は、こんなやり方でも良いのかも。