はじめに
UNIX/Linuxコマンドで有名なテキスト処理系のコマンドjq
, grep
, awk
, sed
などがありますが、
使うとき毎回毎回調べながら使っているので、もっと即興で使いこなせるようになればすごく作業効率が上がるのでないかと思い、これらのコマンドのマニュアルを眺めながら1から勉強してみようと思いました。
その第一回目はjq
ですが、jqだけでも膨大な量のマニュアルでした笑。
今回はjsonをフィルタリングしたり、整形したりする際の基本的な文法だけを学んでいきます。
環境
システムのバージョン:macOS 10.15.7 (19H2)
カーネルのバージョン:Darwin 19.6.0
jqのバージョン :jq-1.6
参考文献
man jq
Filter系
そのまま使う
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq
cat tmp.json | jq '.' # 同じ出力
{
"message": "Hello World",
"colors": [
"yellow",
"red",
"blue"
],
"members": [
{
"name": "Tom",
"age": 20
},
{
"name": "Bob",
"age": 31
}
]
}
この時の.
は入力されたjsonの一番トップレベルの階層を表します。
つまり、入力されたjsonそのものです。
Objectを扱う
キーを指定する(その1)
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.message'
"Hello World"
キー"message"
に対応する値"Hello World"
だけを取り出しました。
cat tmp.json | jq '.hoge'
null
存在しないキーを選択するとnullが返ります。
キーを指定する(その2)
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.["members"]'
[
{
"name": "Tom",
"age": 20
},
{
"name": "Bob",
"age": 31
}
]
このように["キー名"]
でも取り出せます。
.members
と .["members"]
は同じ意味ですね。
値だけ列挙する
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.[]'
"Hello World"
[
"yellow",
"red",
"blue"
]
[
{
"name": "Tom",
"age": 20
},
{
"name": "Bob",
"age": 31
}
]
複数キーを指定する
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.message, .colors'
"Hello World"
[
"yellow",
"red",
"blue"
]
カンマ,
で複数のキーを指定できます。
Arrayを扱う
Arrayの中身を列挙
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.colors'
[
"yellow",
"red",
"blue"
]
このcolorsの3つの要素を列挙する形で取り出したい時は、
cat tmp.json | jq '.colors[]'
"yellow"
"red"
"blue"
.colors
だけだとリストそのものを表示するのに対して、
[]
を後ろにつけることによってリストの中身を全て列挙するようになります。
Arrayからn番目の要素
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.members[0]'
{
"name": "Tom",
"age": 20
}
.members[0]
で0番目の要素を取り出しました。
cat tmp.json | jq '.members[2]'
null
存在しない要素番号を指定するとnullが返ります。
cat tmp.json | jq '.colors[-1]'
"blue"
負の数を使うことで後ろから数えて1番目を取り出します。
この時"yellow"
は前から数えても後ろから数えても0番目であると考えるとわかりやすいかと思います。
cat tmp.json | jq '.colors[-3]'
cat tmp.json | jq '.colors[-4]'
"yellow"
null
"yellow"
の前には何も存在しないのでnullが返ります。
Arrayから任意のキーを指定して表示
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.members[].name'
"Tom"
"Bob"
members
のように入れ子になった構造で、nameだけ取り出したい時は上記のようにします。
Arrayから取り出す範囲を指定する(Slice)
["a","b","c","d","e"]
cat tmp.json | jq '.[1:3]'
cat tmp.json | jq '.[:3]'
cat tmp.json | jq '.[2:]'
cat tmp.json | jq '.[-3:-1]'
["b","c"]
["a","b","c"]
["c","d","e"]
["c","d"]
指定したキーがArrayでなくてもエラーにしない
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.message[]'
cat tmp.json | jq '.message[]?'
jq: error (at <stdin>:1): Cannot iterate over string ("Hello World")
(何も表示されない)
上の場合だと、messageの値はKey/Value ObjectでもArrayでもないのでエラーになりますが、
下の場合はエラーにしません。
Pipe
{"message":"Hello World", "colors":["yellow","red","blue"],"members":[{"name":"Tom","age":20},{"name":"Bob","age":31}]}
cat tmp.json | jq '.members[] | .name'
cat tmp.json | jq '.members[].name' # 同じ
"Tom"
"Bob"
UNIX/Linuxの |
とイメージ的には同じですね。
これだとあんまり意味ありませんね笑
条件指定
cat tmp.json | jq '.members[] | select (.name == "Bob")'
{
"name": "Bob",
"age": 31
}
上記のような関数を用いて複雑な条件指定をする時に使用したりします。
一括置換
cat tmp.json | jq '.members[] | .age + 5'
25
36
Objectリストの要素を一括置換したりするときとかにも使えます。
再帰検索
{"a":{"b":{"c":{"a":"e"}}}}
cat tmp.json | jq '..'
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
{
"b": {
"c": {
"d": "e"
}
}
}
{
"c": {
"a": "e"
}
}
{
"a": "e"
}
"e"
ここから"a"がキーのものを取り出します。
cat tmp.json | jq '.. | .a?'
{
"b": {
"c": {
"a": "e"
}
}
}
null
null
"e"
nullも消します。
cat tmp.json | jq '.. | .a? | select(. != null)’
{
"b": {
"c": {
"a": "e"
}
}
}
"e"
Pipe気持ちいいですね笑
Operation系
数値の計算
{"a": 7}
cat tmp.json | jq '.a + 1'
cat tmp.json | jq '.a > 1'
8
true
基本的なプログラミング言語にあるような、四則+剰余演算, 比較演算はできます。
[1,2,3,4,5,6]
cat tmp.json | jq '.[] + 1'
2
3
4
5
6
7
リストを一括計算もできます。
リストの加算
{"a" : [1,2], "b" : [2,3]}
cat tmp.json | jq '.a + .b'
[1,2,1,3]
文字列の加算
{"FirstName" : "Hanako", "LastName" : "Tanaka"}
jq '.FirstName + " " +.LastName'
"Hanako Tanaka"
文字列の分割
{"text": "Nothing in life is to be feared. It is only to be understood."}
cat tmp.json | jq '.text / " "'
[
"Nothing",
"in",
"life",
"is",
"to",
"be",
"feared.",
"It",
"is",
"only",
"to",
"be",
"understood."
]
半角スペースを区切り文字として、文字列全体を分割してリスト化しています。
今回はここまで
jq便利すぎる!!
次回はもっと複雑な操作ができる関数や、if文などをやっていくかなーと思います。