AWS CLIの--queryでよく使う、ダッシュ(-)とかをエスケープしたり変数展開する方法まとめ

# はじめに

AWS CLIでは`--query`オプションに対してJMESPathというクエリ言語を渡すことができます。
ダッシュなどの記号をエスケープしたり変数を展開するときにいつも混乱するので、備忘録としてまとめておこうと思います。


# 記号のエスケープ

例として、AWS Glueでダッシュがついたキーを持つJSONを例にあげます。

```shell
$ aws glue get-job --job-name qiita-sample1 --query "Job.DefaultArguments"
{
    "--TempDir": "s3://xxxxxxxxxxxx",
    "--job-bookmark-option": "job-bookmark-disable",
    "--job-language": "python"
}
```

ダッシュをエスケープし、`--job-language`を取得するには以下のように書きます。

```shell
$ aws glue get-job --job-name qiita-sample1
                   --query 'Job.DefaultArguments."--job-language"'

# 結果
"python"
```

`--query`に渡す文字列を、シングルクオーテーション(')で囲み、エスケープしたい部分をダブルクオーテーションで囲っています。シングルクオーテーションで囲むとraw-string-literalsという書き方になるらしいです。

https://jmespath.org/proposals/raw-string-literals.html
詳しくは上記リンクを参照してください。

もしくは通常通りダブルクオートで囲み、内部のダブルクオートを`\`を使ってエスケープすることもできます。

```shell
$ aws glue get-job --job-name qiita-sample1
                   --query "Job.DefaultArguments.\"--job-language\""

# 結果
"python"
```


# 変数の展開

## 通常のJMESPath式で変数展開する場合

クエリ文字列をダブルクオートで囲んでいれば、通常の変数展開の方法が使えます。
普通にshellの機能として変数展開しているだけですね。

```shell
$ echo $VAL1
DefaultArguments

# 方法1
$ aws glue get-job --job-name qiita-sample1 --query "Job.${VAL1}"

# 方法2
$ aws glue get-job --job-name qiita-sample1 --query "Job.$VAL1"
```

注意すべきなのは**フィルタープロジェクションを使った場合です。**
https://jmespath.org/tutorial.html#filter-projections

IAMのgroupをリスト表示し(aws iam list-groups)、そこから特定のGroupNameを持つデータを取得することを想定します。

## 前置き: 変数展開を使わず直接値を書く場合

フィルターするデータはシングルクオートかバッククオートで囲む必要があります。ただし、有効なのは以下の書き方だけです。
GroupNameが`testgroup`のデータを取得します。

```shell
# ダブルクオートの中でシングルクオートを使うパターン。
$ aws iam list-groups --query "Groups[?GroupName=='testgroup']"

# シングルクオートの中なら、バッククオートをそのまま使用できる。
$ aws iam list-groups --query 'Groups[?GroupName==`testgroup`]'

# ダプルクオート中でバッククオートを使うには、エスケープする必要がある。
$ aws iam list-groups --query "Groups[?GroupName==\`testgroup\`]"
```

フィルターする対象の文字列(testgroup)は必ずシングルorバッククオートで囲む必要がある、というのが混乱ポイントです。それを前提として、クエリ文字列自体が通常モード(")か、Row string(')なのかを考えます。

## 変数を展開する場合

以下の書き方が有効です。

```shell
# ダブルクオートの中でシングルクオートを使うパターン。
$ aws iam list-groups --query "Groups[?GroupName=='$VAL']"
$ aws iam list-groups --query "Groups[?GroupName=='${VAL}']"

# ダプルクオート中でバッククオートを使うには、エスケープする必要がある。
# 変数の中身が「数値型」の場合、この書き方にする必要がある。(シングルクオートだと文字列になってしまう)
$ aws iam list-groups --query "Groups[?GroupName==\`${VAL}\`]"
```

**以下の書き方はなんとなくいけそう(個人的な感想)ですが、返り値が空のリストになってしまいます。**

```shell
# これはできない！！
$ aws iam list-groups --query 'Groups[?GroupName==${VAL}]'
$ aws iam list-groups --query 'Groups[?GroupName==`${VAL}`]'
```

--queryに渡っているのはあくまでshellの文字列なので、シングルクオートだと文字列になってしまって変数展開されないんですね。（変数展開はシェルの機能なので)

最初に記載した「記号のエスケープ」を見ると何となくJMESPath特有の方法でパースされている...ように思うんですが、変数展開はJMESPathでパースされるより前の段階なのでごく当たり前の挙動になるというイメージだと思っています(違ったらご指摘オナシャス)

shellの文字列として解釈=> そのあとJMESPathの式として解釈...という順番だと分かればなんてことはないのですが、頭ではわかっていても書いていると自分は混乱してきます。




# 一番いい書き方は？

とりえあえず`--query`に渡す文字列はダブルクオートで囲んでおくのが一番いいと個人的には感じています。シングルだと、後から変数展開使う方法に書き換えるのが手間なので...
フィルターで使う変数の展開はシングルクオートを使う方が、バッククオートより書きやすいかなと思います。

**2021/01/24追記 こちら、展開する変数の中身が数値の場合(文字列でない場合)、エスケープしたバッククオートを使う必要があると気がつきました...**

他のJMESPathの文法にもひょっとするとぶつかってくるかもしれないので、とりあえず有効なパターンとその理由は抑えておいても損はないかなと思いました。

# 参考リンク

JMESpath本家のリンク。わかりやすいです。
https://jmespath.org/

<br>

`--query`についてはこちらの勉強会に参加させていただいた時に知りました。圧倒的感謝。

https://jawsug-cli.connpass.com/