Closed45
『jqハンドブック』をやっていく会
README
- jqハンドブック―NetOps/DevOps必携のJSONパーザ
-
stoyosawa/jqDoc-public: jqハンドブック
- 公式リポジトリ(サンプルデータがある!)
進め方
- つまみ食いで行く
- 3〜4回くらいで終わらせる
開催実績
- 2021/12/06(月) https://zenn.dev/link/comments/b642aae141f972
- 2021/12/13(月) https://zenn.dev/link/comments/0a9d47c8f2b0a4
- 2021/12/20(月) https://zenn.dev/link/comments/d05a74fbf44183
- 2022/01/03(月) https://zenn.dev/link/comments/92fd489fb6e847
感想
- 個別にバラバラ読んでいく&試していくスタイルは良かった! 直列音読じゃなくてよかったと思う
-
[]
はイテレータという衝撃と学び -
jq
にプログラミングは求めていないですね- 「色付きコマンドラインJSON Viwer プラスα」という感じにおちついた
- 「○○コマンドで良いのでは?」がめっちゃ多かったw
感想
- jqコマンドは大体レスポンスの色付け係の位置づけだったので、ここまで高機能なのはびっくりした
- 逆にどういう経緯でここまでの機能を作ったのだろう?
- ○○コマンドで良いのでは? という代替手段が出せるということは、それだけ引き出しを持っている
2021/12/06(月)
やったこと
- 1章 つまみ食い
- 2章 つまみ食い
- 3章 つまみ食い
MEMO
- 「ソロで読む → 理解する → LTする」パターンが良さそう!
- イテレータの気持ちがわかってとても嬉しい
- 再帰が楽しい
- なかじまさんのシェルテクニックは毎回参考になる!
sed -n
次回開催メモ
- 4章から
.
is 何 ?
-
.
は filterに属する - 省略したら、補われるのか? それとも、なくても動くのか? 必須っぽいけど?
$ jq [options] filter file.json
$ cat startMeUp01.json | jq '.,.,.,.'
[
"烧餐包",
"烧卖皇",
"虾饺",
"韭菜饺"
]
[
"烧餐包",
"烧卖皇",
"虾饺",
"韭菜饺"
]
[
"烧餐包",
"烧卖皇",
"虾饺",
"韭菜饺"
]
[
"烧餐包",
"烧卖皇",
"虾饺",
"韭菜饺"
]
$ cat startMeUp02.json | jq '.contact'
{
"phone": "09-123-4567",
"メール": "booking@starcafe.com"
}
$ cat startMeUp02.json | jq '.contact.メール'
jq: error: syntax error, unexpected INVALID_CHARACTER, expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.contact.メール
jq: 1 compile error
$ cat startMeUp02.json | jq '."contact.メール"'
null
[]
はただ[]
を外すだけと思いきや、イテレータだったのか
1.2.5 # 1つの配列だった!
$ cat startMeUp01.json | jq '.'
[
"烧餐包",
"烧卖皇",
"虾饺",
"韭菜饺"
]
# 4つのJSONだったのか!
$ cat startMeUp01.json | jq '.[]'
"烧餐包"
"烧卖皇"
"虾饺"
"韭菜饺"
# 後ろに関数をつなげると、よりわかりやすい
$ cat startMeUp01.json | jq '.[] | length'
3
3
2
3
イテレータパターンがとてもよくわかる
配列でもオブジェクトでも、とにかく順次取り出すってこと!
$ cat startMeUp02.json| jq '.contact'
{
"phone": "09-123-4567",
"メール": "booking@starcafe.com"
}
$ cat startMeUp02.json| jq '.contact[]'
"09-123-4567"
"booking@starcafe.com"
leafだけ取るっぽい感じのやつ
$ cat startMeUp02.json| jq '.. | scalars'
"別不同"
false
4.5
"11:00"
"15:00"
"09-123-4567"
"booking@starcafe.com"
インデントに上限あり!
$ jq --indent 17 '.' startMeUp02.json
jq: --indent takes a number between -1 and 7
Use jq --help for help with command-line options,
or see the jq manpage, or online docs at https://stedolan.github.io/jq
-c
コンパクト出力で、イテレータのことをもっと理解できる
$ jq -c '.[]' startMeUp01.json
"烧餐包"
"烧卖皇"
"虾饺"
"韭菜饺"
$ cat startMeUp02.json | jq '.[]'
"別不同"
false
4.5
[
"11:00",
"15:00"
]
{
"phone": "09-123-4567",
"メール": "booking@starcafe.com"
}
# コレクション型がコンパクト(1行になる) → 合計5行
$ cat startMeUp02.json | jq -c '.[]'
"別不同"
false
4.5
["11:00","15:00"]
{"phone":"09-123-4567","メール":"booking@starcafe.com"}
$ cat startMeUp02.json | jq -c -j '.[]'
別不同false4.5["11:00","15:00"]{"phone":"09-123-4567","メール":"booking@starcafe.com"}
# スカラー型は1行になるが、コレクション型は1行になるわけじゃない。そうしたいなら-cしてください
$ cat startMeUp02.json | jq -j '.[]'
別不同false4.5[
"11:00",
"15:00"
]{
"phone": "09-123-4567",
"メール": "booking@starcafe.com"
}
「パイプ」違い
こっちのパイプはシェルの「パイプ」
↓
$ cat startMeUp01.json | jq '. | join(",")'
↑
こっちは jq の方の「パイプ」
ストリームデータの処理ができるのえらい
$ printf '\036"dim sum"\n\036"yum cha"\n' | jq '.' | od -t x1c
0000000 22 64 69 6d 20 73 75 6d 22 0a 22 79 75 6d 20 63
" d i m s u m " \n " y u m c
0000020 68 61 22 0a
h a " \n
0000024
$ printf '\036"dim sum"\n\036"yum cha"\n' | jq --seq '.' | od -t x1c
0000000 1e 22 64 69 6d 20 73 75 6d 22 0a 1e 22 79 75 6d
036 " d i m s u m " \n 036 " y u m
0000020 20 63 68 61 22 0a
c h a " \n
0000026
jq
は標準入力が前提だけど、それをNULLってことにしちゃうことで、jq
の練習がいろいろできるって感じ
o --null-input/-n:
Don't read any input at all! Instead, the filter is run once using
null as the input. This is useful when using jq as a simple calcu-
lator or to construct JSON data from scratch.
$ echo '{"name": "Bob", "age": 25}' | jq .
{
"name": "Bob",
"age": 25
}
# ちょくでかけるので、ちょっと楽かも
$ jq -n '{"name": "Bob", "age": 25}'
{
"name": "Bob",
"age": 25
}
..
(ダブルドット)楽しい
$ echo [[[[1,2,3]]]] | jq '.[0][0][0][0]'
1
$ echo [[[[1,2,3]]]] | jq '..'
[
[
[
[
1,
2,
3
]
]
]
]
[
[
[
1,
2,
3
]
]
]
[
[
1,
2,
3
]
]
[
1,
2,
3
]
1
2
3
$ echo [[[[1,2,3]]]] | jq -c '..'
[[[[1,2,3]]]]
[[[1,2,3]]]
[[1,2,3]]
[1,2,3]
1
2
3
$ echo [[[[1,2,3]]]] | jq '.. | numbers'
1
2
3
cat filter01.json | jq '[ .[]."品目"[] ] [3:8]'
[
"納豆巻",
"びんちょう",
"〆さば",
"あじ",
"いわし"
]
jq使わないパターン
$ cat filter01.json | jq '.[]."品目"[]' | sed -n 4,8p
"納豆巻"
"びんちょう"
"〆さば"
"あじ"
"いわし"
再帰とフィルタで楽しい
$ cat filter01.json| jq '.. | strings'
"こはだ"
"とびっこ軍艦"
"とろたく軍艦"
"納豆巻"
"びんちょう"
"〆さば"
"あじ"
"いわし"
"うなぎ"
"あわび"
"中トロ"
"いくら軍艦"
# 配列にすれば、スライス演算可能
$ cat filter01.json| jq '[.. | strings][3:8]'
[
"納豆巻",
"びんちょう",
"〆さば",
"あじ",
"いわし"
]
2021/12/13(月)
やったこと
- 4章から
- 5章
- 制御構造とかWHERE句的な操作でがちゃがちゃ
MEMO
- 数値演算とかあるけど、使い所がいまいち!
-
$ jq -n '.is_created = "true"'
とかでJSONをつくるのは便利かも! - 特定条件にマッチするオブジェクトの抽出がわからんかった!
- FizzBuzzできた! => 完全理解!
次回開催メモ
- 6章から
関数っていうか、パイプ演算子感って感じ!
$ echo '"そばや"' | jq '. | length'
3
$ echo '"そばや"' | jq 'length'
3
# numberだけ抽出
$ cat type01.json| jq '.number'
[
17,
3.14,
6.67e-11
]
$ echo '["Alice", "Bob", "Cathy"]' | jq '.[]'
"Alice"
"Bob"
"Cathy"
$ echo '["Alice", "Bob", "Cathy"]' | jq '.[] | length'
5
3
5
$ cat type01.json| jq '.number | .[5] = .[0] + .[1]'
[
17,
3.14,
6.67e-11,
null,
null,
20.14
]
numberのarrayを取った上で、イテレータで回して すべて10倍する
$ cat type01.json | jq '.number | .[] | . * 10'
170
31.400000000000002
6.67e-10
|=
更新代入
$ cat type01.json | jq '.number | .[] |= . * 10'
[
170,
31.400000000000002,
6.67e-10
]
$ echo {} | jq '.is_created = "true"'
{
"is_created": "true"
}
# pythonでいうとこういう感じってこと
>>> d = {}
>>> d['is_created'] = True
# NULL input 使ったほうがいい
$ jq -n '.is_created = "true"'
{
"is_created": "true"
}
jq
を使ってJSONのサンプルデータを作る!
$ jq -n '.name="Alice" | .age=25 | .skills.programming=["Python", "Go", "C"] | .skills.cooking=["a", "b"] '
{
"name": "Alice",
"age": 25,
"skills": {
"programming": [
"Python",
"Go",
"C"
],
"cooking": [
"a",
"b"
]
}
}
0から始まり。
$ jq -n 'range(5)'
0
1
2
3
4
dateコマンドでよいのでは
$ jq -n 'now | todateiso8601'
"2021-12-13T14:10:05Z"
seq
より高機能な感じがある...?
$ jq -n 'range(5)' | jq '. |= .*10'
時刻に関する操作は使い所がありそう!
数値演算は、使いどころがあるのか? どういうモチベ?
if文とかあるんか!?w
# tostring(.) ではないところに注意
$ cat type01.json | jq '.number[] | if . > 10 then tostring + " is Yes" else tostring + " is No" end'
"17 is Yes"
"3.14 is No"
"6.67e-11 is No"
.
で イテレータの各要素を取得できる。
tostringは .
無くても勝手に解釈されるっぽい
$ cat type01.json | jq '.number[] | if . > 16 then tostring + " > 16 = true" else tostring + " > 16 = false" end'
"17 > 16 = true"
"3.14 > 16 = false"
"6.67e-11 > 16 = false"
FizzBuzzできた!
$ seq 15 | jq -r '. | if . % 15 == 0 then "FizzBuzz" elif . % 5 == 0 then "Buzz" elif . % 3 == 0 then "Fizz" else . end'
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
もしくはこう
$ jq -nr 'range(16) | if . % 15 == 0 then "FizzBuzz" elif . % 5 == 0 then "Buzz" elif . % 3 == 0 then "Fizz" else . end'
2021/12/20(月)
やったこと
- 6章〜8章
MEMO
- jqに関する印象がかなり変わった! フィルタっていうかもう、プログラミング言語って感じ。
- なぜ、jqにいろんな機能があるんだぜ?
- と、思ったけど、JSONのフィルタリングや変換や集計タスクを考えると、必要な機能ばっかりじゃん! すまんjq!
- 「(俺が必要だとは思わないから)この機能いらなくね?」って思ったけど、ユーザーは俺だけじゃなかった!ww それに、別ツールで出来ちゃうってのがあるよね。
次回開催メモ
- 9章
Unicodeとかの表記
-
explode
関数 -
--ascii-output
,-a
オプション
$ cat string01.json | jq '.[5][:5] | explode'
[
91, # [ 0x5b
26085, # 日 0x65e5
26412, # 本 0x772c
37202, # 酒 0x9152
93 # ] 0x5d
]
$ cat string01.json | jq -a '.[5][:5]'
"[\u65e5\u672c\u9152]"
$ cat string01.json | jq --ascii-output '.[5][:5]'
"[\u65e5\u672c\u9152]"
scanは使うかも
$ cat string01.json | jq '.[]'
"[White] Albert Bichot Chablis"
"[Red] albert bichot nuit-saint-georges"
"[White] Pasqua Soave Classico"
"[Red] Pasqua Chanti classico"
"[日本酒] 雨後の月 吟醸純米酒"
"[日本酒] 雨後の月 斗瓶取り"
"[日本酒] 五橋 純米吟醸 西都の雫"
"[日本酒] 五橋 斗瓶取り"
"🍷"
$ cat string01.json | jq '.[] | scan("\\w*酒\\w*")'
"日本酒"
"吟醸純米酒"
"日本酒"
"日本酒"
"日本酒"
特定の配列の先頭要素と末尾要素を同時に取得できるのは便利っぽい(時刻データなら集計期間とか分かる感じ)
$ cat array01.json| jq -r '.set[0] | first, last'
ひれかつ
ロースかつ鍋
パイプからじゃなくて、直接関数を呼ぶときはイテレータを渡す
$ cat array01.json | jq 'first(.set[0][])'
index
関数
$ echo [1250,1150,1450,1150] | jq '. | index(1150)'
1
$ echo [1250,1150,1450,1150] | jq '. | rindex(1150)'
3
$ echo [1250,1150,1450,1150] | jq '. | indices(1150)'
[
1,
3
]
flatten
も使いそう
$ cat array01.json| jq '.set'
[
[
"ひれかつ",
"ロースかつ",
"海老フライ",
"ロースかつ鍋"
],
[
"御飯",
"定食"
]
]
$ cat array01.json| jq '.set | flatten'
[
"ひれかつ",
"ロースかつ",
"海老フライ",
"ロースかつ鍋",
"御飯",
"定食"
]
combination
も使いどころありそう
$ cat array01.json| jq -c '.set'
[["ひれかつ","ロースかつ","海老フライ","ロースかつ鍋"],["御飯","定食"]]
$ cat array01.json| jq -c '.set | combinations'
["ひれかつ","御飯"]
["ひれかつ","定食"]
["ロースかつ","御飯"]
["ロースかつ","定食"]
["海老フライ","御飯"]
["海老フライ","定食"]
["ロースかつ鍋","御飯"]
["ロースかつ鍋","定食"]
$ cat array01.json| jq -c '.set | combinations | join("")'
"ひれかつ御飯"
"ひれかつ定食"
"ロースかつ御飯"
"ロースかつ定食"
"海老フライ御飯"
"海老フライ定食"
"ロースかつ鍋御飯"
"ロースかつ鍋定食"
$ echo '[1, 2, 3]' | jq 'reduce .[] as $x (0; . = . + $x)'
6
$ echo '[1, 2, 3]' | jq -c '.[]' | xargs | tr ' ' '+' | bc
6
jqに頼らないver
$ echo '[1, 2, 3]' | tr -d "[" | tr -d "]" | tr ',' '+' | bc
エントリー化
to_entries
関数はプロパティを名前と値に分け、それぞれkeyとvalueというプロパティに収容して返します。オブジェクトには複数のプロパティが含まれているので、関数は複数のオブジェクトを含んだ配列を返します。関数引数はありませんこの処理を「エントリー化」と呼びます。
$ jq -n '{"size": "並盛"} | to_entries'
[
{
"key": "size",
"value": "並盛"
}
]
$ jq -n '{"size": "並盛", "price":352} | to_entries'
[
{
"key": "size",
"value": "並盛"
},
{
"key": "price",
"value": 352
}
]
再帰取得
$ cat object01.json | jq -c '.. '
{"curry":{"name":"牛だく牛カレー","price":598,"energy":"774"},"焼肉":{"name":"牛焼肉定食","price":598,"energey":785,"sides":["rice","salad","miso soup"]},"bowl":{"name":"牛丼","price":352,"energy":635}}
{"name":"牛だく牛カレー","price":598,"energy":"774"}
"牛だく牛カレー"
598
"774"
{"name":"牛焼肉定食","price":598,"energey":785,"sides":["rice","salad","miso soup"]}
"牛焼肉定食"
598
785
["rice","salad","miso soup"]
"rice"
"salad"
"miso soup"
{"name":"牛丼","price":352,"energy":635}
"牛丼"
352
635
$ cat object01.json | jq -c '.. | keys'
["bowl","curry","焼肉"]
["energy","name","price"]
jq: error (at <stdin>:18): string ("牛だく�...) has no keys
objectsでオブジェクトのみを取得
$ cat object01.json | jq -c '.. | objects | keys'
["bowl","curry","焼肉"]
["energy","name","price"]
["energey","name","price","sides"]
["energy","name","price"]
2022/01/03(月)
やったこと
- 9章
- 10章
MEMO
-
jq
の守備範囲の広さ! そしてそこまでいらなくね? -
jq
じゃなくていいじゃん案件 - そもそも
jq
プログラミングする気にはならない話(別言語でやっちゃうよね)
次回開催メモ
なし! 今回で終了! おつかれ!
このスクラップは2022/01/03にクローズされました