jqでシェルの変数を使う

jqでシェルの変数を使おうとしたら意外と思った通り動かなくて苦労したので、備忘録としてまとめておきます。

# フィルタでシェルの変数を使う
`jq '.devices.iphone11.ios'`というjqコマンドの`''`で囲まれた部分をフィルタといいます。

このフィルタのところでシェルの変数を使う例を挙げてみます。

テストデータとして以下のJSONを使います。

```json
{
  "devices": {
    "iphone11": {
      "ios": "iOS 13.0"
    },
    "iphone12": {
      "ios": "iOS 14.0"
    }
  }
}
```

ここで、`iphone11`の`ios`の値を取り出したいときは以下のコマンドを実行します。

```bash
$ cat test.json | jq '.devices.iphone11.ios'
"iOS 13.0"
```

## フィルタをまるごと変数化
フィルタの内容をまるごと変数に格納し、jqコマンドで参照します。

```bash
$ filter=".devices.iphone11.ios"

$ cat test.json | jq $filter
"iOS 13.0"
```

## フィルタの一部を変数化
フィルタの一部だけを変数に格納し、jqコマンドで参照します。

末尾に入るときはクオーテーションの外に出します。

```bash
$ filter="ios"

$ cat test.json | jq '.devices.iphone11.'$filter
"iOS 13.0"
```

フィルタの真ん中などで変数を使いたいときも、こんな感じでクオーテーションの外に出します。

```bash
$ name="iphone11"

$ cat test.json | jq '.devices.'$name'.ios'
"iOS 13.0"
```

jqコマンドにはフィルタに値を渡すオプション`--arg`があるのですが、シェル変数と組み合わせて使おうとすると案外うまくいきません。こういう使い方をするものではないのかな。

```bash
$ cat test.json | jq --arg arg1 $name '.devices.$arg1.ios'
jq: error: syntax error, unexpected '$', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.devices.$arg1.ios
jq: 1 compile error
```

# 変数の値に空白やドットを含む場合
次は以下のJSONデータで考えます。キーに空白やドットが入っているケースです。

```json
{
  "devices": {
    "iphone 11": {
      "ios": "iOS 13.0"
    },
    "iphone.12": {
      "ios": "iOS 14.0"
    }
  }
}
```

`iphone 11`と`iphone.12`の`ios`の値を取り出したいときはそれぞれ以下のコマンドを実行します。

空白などを含むときはフィルタ内でダブルクオーテーションでくくってあげます。

```bash
$ cat test.json | jq '.devices."iphone 11".ios'
"iOS 13.0"

$ cat test.json | jq '.devices."iphone.12".ios'
"iOS 14.0"
```

## フィルタをまるごと変数化
以下のコマンドではうまくいきませんでした。なんかやりようはあるのかな...？

```bash
$ filter='.devices."iphone 11".ios'

$ cat test.json | jq $filter
jq: error: syntax error, unexpected $end, expecting QQSTRING_TEXT or QQSTRING_INTERP_START or QQSTRING_END (Unix shell quoting issues?) at <top-level>, line 1:
.devices."iphone
jq: 1 compile error
```

## フィルタの一部を変数化
空白を含む場合クオーテーションが多くてややこしいですが、それぞれ以下のコマンドで実行できました。

```bash
$ name="iphone 11"

$ cat test.json | jq '.devices."'"$name"'".ios'
"iOS 13.0"

$ name="iphone.12"

$ cat test.json | jq '.devices."'$name'".ios'
"iOS 14.0"
```

# 【応用編】iOSシミュレーターのUDIDを取り出すコマンド
`xcrun simctl list devices -j`というコマンドを実行すると、利用可能なiOSシミュレーターを一覧化したJSONデータが出力されます。

```bash
$ xcrun simctl list devices -j
{
  "devices" : {
    "com.apple.CoreSimulator.SimRuntime.iOS-14-3" : [
      {
        "dataPath" : "\/Users\/takehilo\/Library\/Developer\/CoreSimulator\/Devices\/7DCA532E-D95A-4B73-A389-660E85F85858\/data",
        "logPath" : "\/Users\/takehilo\/Library\/Logs\/CoreSimulator\/7DCA532E-D95A-4B73-A389-660E85F85858",
        "udid" : "7DCA532E-D95A-4B73-A389-660E85F85858",
        "isAvailable" : true,
        "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPad--7th-generation-",
        "state" : "Shutdown",
        "name" : "iPad (7th generation)"
      },
      {
...
```

このJSONデータから、`iPhone 12`の`iOS 14.5`のシミュレーターのUDIDを取り出してみます。

```bash
$ os_version="com.apple.CoreSimulator.SimRuntime.iOS-14-5"
$ model="iPhone 12"

$ xcrun simctl list devices -j | jq -r '.devices."'$os_version'"[] | select(.name == "'"$model"'") | .udid'
97206822-70C1-404B-876E-6F34A65DCA35
```

# まとめ
jqでシェルの変数を使う方法について説明しました。

クオーテーションの扱いがややこしいですが、わかってしまえば使うのは簡単ですね（私はこれがわかるまでなかなか時間かかってしまいました...）。

参考になれば幸いです。