JMESPath を CLI で試せる jp コマンドが便利なのでメモしておく。
JMESPath と jp コマンドってなに?
JMESPath は JSON のクエリ言語の1つで JSON からデータを抽出したり集計するのに使う。
JMESPath は AWS CLI の --query オプションが JMESPath での指定になるので知っておいて損はないとは思う。
自分は普段は AWS CLI の出力は jq コマンドでパースとフィルタリングすることがほとんどなのだけど、たまに jq コマンドがインストールしてなかったりインストールできない環境で AWS CLI の出力をパースしたりフィルタリングするときにすごく困ってしまう。
そこで JMESPath を CLI でいろいろ試せるコマンドを探したら jp コマンドというのがあるみたいで、使ってみたらかなり便利だったのでメモしておく。
jp コマンドのバージョン。
$ jp --version jp version 0.1.3
jp コマンドのインストール
Mac は brew で jp コマンドをインストールできる。
$ brew tap jmespath/jmespath $ brew install jmespath/jmespath/jp
jp コマンドの README は brew install jp でインストールするように記載してるけど brew install jp は https://github.com/sgreben/jp のほうをインストールしてしまうので、JMESPath を扱う jp コマンドのほうは brew install jmespath/jmespath/jp でインストールする必要がある。
README を修正する PR を作ろうとしたらすでに同じ目的の PR があったのだけど2年前の PR がずっと放置されているみたいで、全体的にあまりメンテナンスしていないっぽい。 メンテナンスしてないコマンドを使っていくのは少し不安があるけど jp コマンドのリポジトリをフォークしてるリポジトリは結構あるみたいなので、そのへんはまた探してみるのがよさそう。
jp コマンドの使い方
基本的な使い方
jp コマンドは jp <オプション> <JMESPath の式> みたいにして実行する。
jp コマンドはこんな感じで標準入力から JSON を読みこんで処理する。
$ echo '[0, 1, 2, 3, 5]' | jp @ [ 0, 1, 2, 3, 5 ]
出力をダブルクォートで囲まない
jp コマンドの最終的な出力が文字列のときはダブルクォート (") で囲むけど --unquoted オプションか -u オプションを指定するとダブルクォート (") で囲まなくなる。jp コマンドの出力をパイプでほかのコマンドに接続するときに使うやつ。
$ echo '{"name": "bob"}' | jp name
"bob"
$ echo '{"name": "bob"}' | jp -u name
bob
JP_UNQUOTED 環境変数を設定しておくことでも出力をダブルクォートで囲まなくなる。
$ export JP_UNQUOTED=true
$ echo '{"name": "bob"}' | jp name
bob
JSON をファイルから読み込む
--filename オプションか -f オプションで JSON をファイルから読み込む。
$ cat data.json [0, 1, 2, 3, 5] $ jp -f data.json @ [ 0, 1, 2, 3, 5 ]
JMESPath の式をファイルから読み込む
--expr-file オプションか -e オプションで JMESPath の式をファイルから読み込む。
$ cat data.json [0, 1, 2, 3, 5] $ cat expr.txt [::2] $ cat data.json | jp -e expr.txt [ 0, 2, 5 ]
JMESPath のチュートリアルをやってみる
jp コマンドの手習いとして JMESPath の チュートリアル をやってみる。
- Basic Expressions
- Slicing
- Projections
- List Projections
- Slice Projections
- Object Projections
- Flatten Projections
- Filter Projections
Basic Expressions
$ echo '{"a": "foo", "b": "bar", "c": "baz"}' | jp a
"foo"
$ echo '{"a": {"b": {"c": {"d": "value"}}}}' | jp a.b.c.d
"value"
$ echo '["a", "b", "c", "d", "e", "f"]' | jp [1]
"b"
$ cat data.json
{"a": {
"b": {
"c": [
{"d": [0, [1, 2]]},
{"d": [3, 4]}
]
}
}}
$ cat data.json | jp a.b.c[0].d[1][0]
1
Slicing
$ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [0:5] [ 0, 1, 2, 3, 4 ] $ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [5:10] [ 5, 6, 7, 8, 9 ] $ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [:5] [ 0, 1, 2, 3, 4 ] $ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [::2] [ 0, 2, 4, 6, 8 ] $ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [::-1] [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]
Projections
List and Slice Projections
$ cat data.json
{
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
$ cat data.json | jp 'people[*].first'
[
"James",
"Jacob",
"Jayden"
]
$ cat data.json | jp 'people[:2].first'
[
"James",
"Jacob"
]
Object Projections
$ cat data.json
{
"ops": {
"functionA": {"numArgs": 2},
"functionB": {"numArgs": 3},
"functionC": {"variadic": true}
}
}
$ cat data.json | jp 'ops.*.numArgs'
[
2,
3
]
Flatten Projections
$ cat data.json
{
"reservations": [
{
"instances": [
{"state": "running"},
{"state": "stopped"}
]
},
{
"instances": [
{"state": "terminated"},
{"state": "running"}
]
}
]
}
$ cat data.json | jp reservations[*].instances[*].state
[
[
"running",
"stopped"
],
[
"terminated",
"running"
]
]
Filter Projections
$ cat data.json
{
"machines": [
{"name": "a", "state": "running"},
{"name": "b", "state": "stopped"},
{"name": "b", "state": "running"}
]
}
$ cat data.json | jp "machines[?state=='running'].name"
[
"a",
"b"
]
Pipe Expressions
$ cat data.json
{
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
$ cat data.json | jp 'people[*].first | [0]'
"James"
MultiSelect
$ cat data.json
{
"people": [
{
"name": "a",
"state": {"name": "up"}
},
{
"name": "b",
"state": {"name": "down"}
},
{
"name": "c",
"state": {"name": "up"}
}
]
}
$ cat data.json | jp 'people[].[name, state.name]'
[
[
"a",
"up"
],
[
"b",
"down"
],
[
"c",
"up"
]
]
$ cat data.json | jp 'people[].{Name: name, State: state.name}'
[
{
"Name": "a",
"State": "up"
},
{
"Name": "b",
"State": "down"
},
{
"Name": "c",
"State": "up"
}
]
Functions
$ cat data.json
{
"people": [
{
"name": "b",
"age": 30,
"state": {"name": "up"}
},
{
"name": "a",
"age": 50,
"state": {"name": "down"}
},
{
"name": "c",
"age": 40,
"state": {"name": "up"}
}
]
}
$ cat data.json | jp 'length(people)'
3
$ cat data.json | jp 'max_by(people, &age).name'
"a"
$ cat data.json | jp "myarray[?contains(@, 'foo') == \`true\`]"
[
"foo",
"foobar",
"barfoo",
"barfoobaz"
]