dasel とは
Dasel (short for data-selector) allows you to query and modify data structures using selector strings.
Comparable to jq / yq, but supports JSON, YAML, TOML, XML and CSV with zero runtime dependencies.
互換とは書かれているけど、セレクタの書き方は jq とはかなり違います。TOML への変換も TOML からの変換もサポートしているのが嬉しい。
インストール
brew install dasel
or
go install github.com/tomwright/dasel/cmd/dasel@master
or
その他の方法はドキュメントをどうぞ → https://daseldocs.tomwright.me/installation
基本的な利用イメージ
dasel -r yaml -w xml '.name'
→ 「YAML の入力に対して name
プロパティを抽出して XML で出力してください」
JSON 入力での色々な例
整形するだけ
dasel -r json
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel -r json
{
"members": [
{
"age": 14,
"id": 0,
"name": "Rei"
},
{
"age": 14,
"id": 1,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
]
}
--color
オプションで色を付ける
dasel --color -r json
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json
{
"members": [
{
"age": 14,
"id": 0,
"name": "Rei"
},
{
"age": 14,
"id": 1,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
]
}
セレクタの基本的な記法
.propertyName
でマップのプロパティをたどり、 .[n]
で配列のインデックスをたどる
dasel --color -r json '.members.[1]'
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.[1]'
{
"age": 14,
"id": 1,
"name": "Shinji"
}
フォーマット変換
たぶん、これが一番便利でよく使う。
-w yaml
オプションで出力を YAML に変換する
dasel --color -r json -w yaml '.members.[2]'
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json -w yaml '.members.[2]'
age: 28
cursed: true
id: 2
name: Asuka
-w toml
オプションで出力を TOML に変換する
dasel --color -r json -w toml '.members.[2]'
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json -w toml '.members.[2]'
age = 28.0
cursed = true
id = 2.0
name = "Asuka"
-w xml
オプションで出力を XML に変換する
dasel --color -r json -w xml '.members.[2]'
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json -w xml '.members.[2]'
<doc>
<age>28</age>
<cursed>true</cursed>
<id>2</id>
<name>Asuka</name>
</doc>
.(<key>=<value>)
でフィルタリング
.(age=14)
として、 14 歳の2人だけに絞り込み。
dasel --color -r json '.members.(age=14)' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age=14)' -m
{
"age": 14,
"id": 0,
"name": "Rei"
}
{
"age": 14,
"id": 1,
"name": "Shinji"
}
.(age>=20)
として、20歳以上の大人のみに絞り込む
dasel --color -r json '.members.(age>=20)' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age>=20)' -m
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
比較演算子は =
と >=
のほかに >
, <
, <=
, !=
と一通りがあります。
フィルタリングした結果をさらに .
でたどる
dasel --color -r json '.members.(age=14).name' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age=14).name' -m
"Rei"
"Shinji"
厳密にいうと、フィルタリングと呼んだ .(<key>=<value>)
は ダイナミックセレクタ という、特殊だけどセレクタの1種です。
フィルタリング条件を複数並べると AND 条件となる
.(age<20)(id>0)
として、未成年で id が正の数のメンバーを抽出。
dasel --color -r json '.members.(age<20)(id>0)' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age<20)(id>0)' -m
{
"age": 14,
"id": 1,
"name": "Shinji"
}
色々なセレクタの記法
.-
セレクタでマップの Key 一覧を出力
dasel --color -r json '.members.[1].-' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.[1].-' -m
"id"
"name"
"age"
.-
セレクタは 配列 の場合は Index 一覧が出力される。
dasel --color -r json '.members.-' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.-' -m
"0"
"1"
"2"
※ .-
セレクタは -m
(--multiple
) オプションの使用が必須なので注意。
.[*]
セレクタでマップの Value 一覧を出力
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.[1].[*]' -m
1
"Shinji"
14
.[*]
セレクタは 配列 の場合も Value 一覧が出力される。
dasel --color -r json '.members.[*]' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.[*]' -m
{
"age": 14,
"id": 0,
"name": "Rei"
}
{
"age": 14,
"id": 1,
"name": "Shinji"
}
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
※ .[*]
セレクタも -m
(--multiple
) オプションの使用が必須なので注意。
.[#]
セレクタで要素数を出力
dasel --color -r json '.members.(age<20)(id>0).[#]' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age<20)(id>0).[#]' -m
3
- 配列の場合は、そのまま要素数
- マップの場合は、プロパティ数
- 文字列の場合は、文字数
.[#]
セレクタじゃなくて --lenghth
オプションでも同様に要素数が得られる。
dasel --color -r json '.members.(age<20)(id>0)' -m --length
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.(age<20)(id>0)' -m --length
3
.[@]
でデータ型を取得する
dasel --color -r json '.members.[@]' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel --color -r json '.members.[@]' -m
"array"
- 複合的な型は
array
,map
の2つ - スカラ型は
string
,int
,float
,bool
の4つ
高度なセレクタ
.(?:<key>=<value>)
で再帰的な検索
※ -m
(--multiple
) オプションの使用が必須なので注意。
例を少し複雑にして friends
プロパティに "Hikari"
さんを追加します。 入力 JSON の全体構造は次のとおり。
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true, "friends": [{"name": "Hikari", "age": 14}]}]}' \
| dasel --color -r json -m
{
"members": [
{
"age": 14,
"id": 0,
"name": "Rei"
},
{
"age": 14,
"id": 1,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"friends": [
{
"age": 14,
"name": "Hikari"
}
],
"id": 2,
"name": "Asuka"
}
]
}
.(?:.age=14)
として、14歳のオブジェクト(マップ)を再帰的に検索
dasel --color -r json '.members.(?:age=14).name' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true, "friends": [{"name": "Hikari", "age": 14}]}]}' \
| dasel --color -r json '.members.(?:age=14).name' -m
"Rei"
"Shinji"
"Hikari"
.members.(?:age=14).name
ではなく、
.members.(age=14).name
というふうに単なるダイナミックセレクタにしてしまうと、再帰的には処理されないのが違い。
次の例では "Hikari"
は1段ネストした先にいるので、出力されていないことが分かる。
dasel --color -r json '.members.(age=14).name' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true, "friends": [{"name": "Hikari", "age": 14}]}]}' \
| dasel --color -r json '.members.(age=14).name' -m
"Rei"
"Shinji"
.(?:<key>=<value>)
の検索で使える演算子は =
と !=
のみ。
put
サブコマンドで要素の追加や更新をする
dasel put <type> <selector> <value>
のような感じでデータ型の指定と 追加/更新 要素の値を渡す。ちなみにサブコマンド無指定の場合は、実は暗黙的に select
サブコマンドが使われている。なのでこれまでの例は全て select
サブコマンドを明示的に指定しても同じ。
※ put
サブコマンドでは -f
/--file
で入力ファイル指定をした場合に、-o
/--out
を使って出力先を指定していないと、入力ファイル自体を更新してしまう(破壊的!)ので注意。 ファイルではなく標準出力に吐きたい場合は -f stdout
/-f -
で OK。
int '.members.[*].age' 20 -m
として、全員の年齢を20歳に更新
dasel put -r json int '.members.[*].age' 20 -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel put -r json int '.members.[*].age' 20 -m
{
"members": [
{
"age": 20,
"id": 0,
"name": "Rei"
},
{
"age": 20,
"id": 1,
"name": "Shinji"
},
{
"age": 20,
"cursed": true,
"id": 2,
"name": "Asuka"
}
]
}
int '.members.(age<20).age' 20 -m
として、未成年のみ全員を20歳に更新
dasel put -r json int '.members.(age<20).age' 20 -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel put -r json int '.members.(age<20).age' 20 -m
{
"members": [
{
"age": 20,
"id": 0,
"name": "Rei"
},
{
"age": 20,
"id": 1,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
]
}
bool '.members.(age<20).is_young' true -m
として、未成年のオブジェクト(マップ)に "is_young": true
プロパティをセット
dasel put -r json bool '.members.(age<20).is_young' true -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel put -r json bool '.members.(age<20).is_young' true -m
{
"members": [
{
"age": 14,
"id": 0,
"is_young": true,
"name": "Rei"
},
{
"age": 14,
"id": 1,
"is_young": true,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
}
]
}
document '.members.[]' '{"name": "Kaworu"}' -m
として、配列の末尾に新しいメンバーを追加
.[]
のセレクタは put
サブコマンドでしか使えないので注意。
jq で .members[]
とするセレクタは dasel では .members.[*]
となる。
dasel put -r json document '.members.[]' '{"name": "Kaworu"}' -m
echo '{"members": [{"id": 0, "name": "Rei", "age": 14}, {"id": 1, "name": "Shinji", "age": 14}, {"id": 2, "name": "Asuka", "age": 28, "cursed": true}]}' \
| dasel put -r json document '.members.[]' '{"name": "Kaworu"}' -m
{
"members": [
{
"age": 14,
"id": 0,
"name": "Rei"
},
{
"age": 14,
"id": 1,
"name": "Shinji"
},
{
"age": 28,
"cursed": true,
"id": 2,
"name": "Asuka"
},
{
"name": "Kaworu"
}
]
}
おまけ: select サブコマンドのオプション一覧
-s |
--selector string |
データ構造をクエリするときに使用するセレクタ。明示しない場合は第一引数を勝手にセレクタと解釈してくれるので、必須指定だけどあまり使わない。 |
-r |
--read string |
読み込み に使用するパーサー。 書いてる時点でサポートされているのは json , toml , yaml , xml , csv , plain
|
-w |
--write string |
書き込み に使用するパーサー。 書いてる時点でサポートされているのは json , toml , yaml , xml , csv , plain
|
-p |
--parser string |
-r と -w をまとめて指定したのと同じ。 |
--colour |
色付け表示。 | |
--color |
--colour の別名。 | |
--c |
--compact |
できる限りプリティプリントを除去して出力をコンパクトにする。 |
--escape-html |
出力を書くときに HTML タグをエスケープする。 | |
--f |
--file string |
クエリするファイル。 |
--format string |
結果を書くときに使用するフォーマットテンプレート。 | |
--length |
結果の要素数を数字で出力。 | |
--merge-input-documents |
複数の入力文書を配列に結合。 | |
-m |
--multiple |
複数の結果を選択。単一の結果の場合にエラーになるわけでもなく基本的に常に指定しておけばいい気がする。 |
-n |
--null |
値が見つからないエラーの代わりに null を出力。 |
--plain |
-w plain の別名。 | |
-h |
--help |
選択した項目のヘルプ |
個人的な Tips
-
-m
は常時指定でいいと思う。結果が複数のときに指定してないと最初の1つだけになってしまうので。 -
put
の場合は-f
や--file
は使わず、パイプで入力を渡すほうが安心。-o
/--out
の指定忘れで意図せず入力元ファイルを更新されるのを防げるから。 - クエリは jq や yq のほうが使いやすい気がする(慣れてるだけかも)。フォーマット変換が必要なときにパイプでつないで dasel を使う、とか組み合わせて使う前提が良さそう。dasel は TOML に変換できるのがありがたい。