調査などである値のリスト出力して他のリストと付け合わて比較したいみたいな場合のメモ書き
- users.json
[
{
"name": "foo1",
"description": "bbbbb"
},
{
"name": "foo2",
"description": "aaaaa"
},
{
"name": "bar1",
"description": "ccccc"
},
{
"name": "bar2",
"description": "ddddd"
},
{
"name": "baz1",
"description": "eeeee"
},
{
"name": "baz2",
"description": "fffff"
}
]
- names.txt
foo1 foo2 bar2
users.jsonの中からnames.txtのリストに含まれるかチェックし含まれないものだけ抜き出したい
コマンドラインに貼り付けるなり何なりで直接入力する場合次のような感じで実現できる
$ cat users.json | jq --arg list '["foo1","foo2","bar2"]' '.[]|select([.name]|inside([$list])|not)'
{
"name": "bar1",
"description": "ccccc"
}
{
"name": "baz1",
"description": "eeeee"
}
{
"name": "baz2",
"description": "fffff"
}
サンプル程度の数量であればコマンドラインで直接入力しても問題ないが量が多くなると結構厳しくなってくる
ちょうど1つ1つコマンドラインに入力していくのはだるい量のリストをjq内で使いたい場面があったので調べてみたらすぐ見つかった
いくつかパターンがあったので残しておく
下準備
その前に下準備
names.txtそのままだとjqの-sでも読み込めないため"で囲む
$ cat names.txt | sed -e 's/$/"/g; s/^/"/g' "foo1" "foo2" "bar1"
$ cat names.txt | sed -e 's/$/"/g; s/^/"/g' | jq -sc ["foo1","foo2","bar1"]
$ cat names.txt | sed -e 's/$/"/g; s/^/"/g' | jq -sc > names.json
細かく試してみると次のような感じ
$ echo 'hoge' | jq parse error: Invalid numeric literal at line 2, column 0 $ echo '"hoge"' | jq "hoge" $ echo '"hoge"' | jq -s [ "hoge" ]
sedで愚直にやらずとも次のような生成の方法もあるらしい
$ jq -ncR '[inputs]' <<< $(cat names.txt) ["foo1","foo2","bar1"]
jqへの値の渡し方いろいろ
調べた中では次の3パターンがあった
--argで値を渡す--argjsonで値を渡す--slurpfileで値を渡す
それぞれ見てみる
1. --argで値を渡す
catで展開した値を--argで渡して後のselect内で配列に変換して比較する
$ cat names.json ["foo1","foo2","bar1"]
コマンドライン自体はスッキリしてよい
$ cat users.json| jq --arg list "$(cat names.json)" '.[]|select([.name]|inside([$list])|not)' | jq '.name' "bar2" "baz1" "baz2"
insideの中で[]を使って配列として扱うようにさせている
--arg自体は別にJSON形式の引数を期待しているわけではないので何でも渡せる
渡した時点での解釈は文字列なので配列に変換してあげる必要がある
2. --argjsonで値を渡す
--argjsonだと引数で渡した値を最初からjsonとして解釈してくれる
$ cat users.json| jq --argjson list "$(cat names.json)" '.[]|select([.name]|inside($list)|not)' | jq '.name' "bar2" "baz1" "baz2"
なので1と比較するとinsideの中身が違う
inside([$list])
inside($list)
--slurpfileでファイルから値を渡す
slurpはテキストのリストを配列として扱ってくれるものなので配列のjsonを渡すと二重配列として扱われてしまう
$ cat users.json| jq --slurpfile list names.json '$list'
[
[
"foo1",
"foo2",
"bar1"
]
]
そのためflattenでフラットにしてあげる必要がある
$ cat users.json| jq --slurpfile list names.json '.[]|select([.name]|inside($list|flatten)|not)' | jq '.name' "bar2" "baz1" "baz2"
そういう話でいうと--slurpfileを使うならダブルクオートで囲ったテキストのリストを渡すのが一番素直
まとめ
ファイルから値を読み込んで色々比較するための方法を3つ試してみた
今回の例でいうとinsideで比較する箇所での扱いがそれぞれ微妙に差分が出た
| --arg | inside([$list]) |
| --argjson | inside($list) |
| --slurpfile | inside($list|flatten) |
それぞれ特徴があるので使いこなしていきたい
個人的にはこのパターンの使い方だったら--argjsonかなと思った
参考:
How to convert string list to JSON string array in Bash? - Stack Overflow