jqコマンドでカジュアルに○○する

# はじめに

jsonをモニョモニョするのに[jq](https://stedolan.github.io/jq/)というツールがありますが、使い始めるとAPI確認したり叩いたりするのにとても役に立ちます。
自分は大体以下のようなときに使います。

- jsonを見やすくする（本記事対象外） 
- indentedなjsonを一行にする（`jq -c .`）
- jsonをパラメータに引き渡す／結果から取り出すための前処理をする
  - jsonの中に文字列としてjsonフォーマットのデータを入れる（`jq '. | @json'`）
  - jsonの中からjsonを取り出す（`jq '. | fromjson`）
- jqコマンドの結果（の一部）を変数に格納する

## 対象文字列の用意

jsonのサンプルは以下から拝借。
<https://www.ibm.com/support/knowledgecenter/ja/SS9H2Y_7.5.0/com.ibm.dp.doc/json_jsonexamples.html>

以下の通り。

```bash
text_ship_to=$(cat << EOF
{ "name" : "Jane Smith",
  "address" : "123 Maple Street",
  "city" : "Pretendville",
  "state" : "NY",
  "zip"   : "12345"
}
EOF
)

text_bill_to=$(cat << EOF
{ "name" : "John Smith",
  "address" : "123 Maple Street",
  "city" : "Pretendville",
  "state" : "NY",
  "zip"   : "12345"
}
EOF
)

text_base=$(cat << EOF
{ "name"   : "John Smith",
  "sku"    : "20223",
  "price"  : 23.95,
  "shipTo" : ${text_ship_to},
  "billTo" : ${text_bill_to}
}
EOF
)
```

`echo $text_base` した結果がこんなかんじ。

```json:text_base
{ "name"   : "John Smith",
  "sku"    : "20223",
  "price"  : 23.95,
  "shipTo" : { "name" : "Jane Smith",
  "address" : "123 Maple Street",
  "city" : "Pretendville",
  "state" : "NY",
  "zip"   : "12345"
},
  "billTo" : { "name" : "John Smith",
  "address" : "123 Maple Street",
  "city" : "Pretendville",
  "state" : "NY",
  "zip"   : "12345"
}
}
```


## 操作

### indentedなjsonを一行にする（`jq -c .`）

`$text_base`はインデントの入った**pretty**なjsonなので、これを**comapct**にしてみる。

```bash:bash
json_text=$(echo $text_base| jq -c .)
```
ヒアドキュメントを直接渡す場合は、`jq`に `<<` する。

```bash:bash
json_text=$(jq -c . << EOF
{ "name"   : "John Smith",
  "sku"    : "20223",
  "price"  : 23.95,
  "shipTo" : { "name" : "Jane Smith",
               "address" : "123 Maple Street",
               "city" : "Pretendville",
               "state" : "NY",
               "zip"   : "12345" },
  "billTo" : { "name" : "John Smith",
               "address" : "123 Maple Street",
               "city" : "Pretendville",
               "state" : "NY",
               "zip"   : "12345" }
}
EOF
)
```

結果はこんなかんじ

```bash:bash
$ json_text=$(echo $text_base| jq -c .)
$ echo $json_text
{"name":"John Smith","sku":"20223","price":23.95,"shipTo":{"name":"Jane Smith","address":"123 Maple Street","city":"Pretendville","state":"NY","zip":"12345"},"billTo":{"name":"John Smith","address":"123 Maple Street","city":"Pretendville","state":"NY","zip":"12345"}}
```

### jsonをパラメータに引き渡す／結果から取り出すための前処理をする

自分はおもに、AWS CLIでIAM設定するときのPolicyDocumentなど、json形式の引数をjsonの中に埋めたり出したりするのに使います。

#### jsonの中に文字列としてjsonフォーマットのデータを入れる（`jq '. | @json'`）

jsonの中に文字列としてjsonを入れるため、試しに以下をエスケープする。

```bash:bash
# エスケープ
escaped_text_ship_to=$(echo $text_ship_to | jq '. | @json')
escaped_text_bill_to=$(echo $text_bill_to | jq '. | @json')
```

`escaped_text_*` をechoしてみると、`@json`な（jsonの中に入れられる）形式にエスケープされていることが分かる。

```bash:bash
echo $escaped_text_ship_to
echo $escaped_text_bill_to
```

```json:$escaped_text_ship_to
"{\"name\":\"Jane Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"
```

```json:$escaped_text_bill_to
"{\"name\":\"John Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"
```

baseに入れてみる。

```bash
escaped_text_base=$(cat << EOF
{ "name"   : "John Smith",
  "sku"    : "20223",
  "price"  : 23.95,
  "shipTo" : ${escaped_text_ship_to},
  "billTo" : ${escaped_text_bill_to}
}
EOF
)
```

`echo $escaped_text_base | jq` してみた結果。`shipTo`と`billTo`がエスケープされたjsonになってる。

```json:$escaped_text_base
{
  "name": "John Smith",
  "sku": "20223",
  "price": 23.95,
  "shipTo": "{\"name\":\"Jane Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}",
  "billTo": "{\"name\":\"John Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"
}
```

#### jsonの中に文字列として入っているjsonフォーマットのデータを取り出す（`jq '. | fromjson'`）

`$escaped_text_base`のjsonの中の`shipTo`はエスケープされている。

```bash:bash
# shipToを取り出してみる

echo $escaped_text_base | jq '.shipTo'
```

```json:.shipTo
"{\"name\":\"Jane Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"
```

これを`| fromjson`を使ってunescapeしてみる。

```bash:bash
echo $escaped_text_base | jq '.shipTo | fromjson'
```

以下のように取れる。

```json:fromjson
{
  "name": "Jane Smith",
  "address": "123 Maple Street",
  "city": "Pretendville",
  "state": "NY",
  "zip": "12345"
}
```

#### jqコマンドの結果（の一部）を変数に格納する

AWS CLIでコマンド叩いた結果からIdだけ引っ張ってくるのに使ったりします。

まずは対象データの準備。

```bash:bash
input='"{\"name\":\"Jane Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"'
echo $input
```

```bash:target
"{\"name\":\"Jane Smith\",\"address\":\"123 Maple Street\",\"city\":\"Pretendville\",\"state\":\"NY\",\"zip\":\"12345\"}"
```

この `$input`を`jq`にかけて結果を変数に入れます。たとえば`state`をダブルクオートなしで取り出す。

```bash:bash
state=$(echo $input| jq 'fromjson | .state' | sed -e 's/"//g')
echo $state
```

```text:$state
NY
```