0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

jq あんちょこ兼メモ

Posted at

個人的な jq あんちょこ兼メモです。

目次:


値が特定の文字列にマッチするデータを抽出する

$ json='[{"genre":"deep house"}, {"genre": "progressive house"}, {"genre": "dubstep"}]'
$ echo "$json" | jq -c '.[] | select(.genre | contains("house"))'
{"genre":"deep house"}
{"genre":"progressive house"}

参考: “jq match value Code Example”


キーリストを取得

$ cat hash.json | jq 'keys'

ハッシュリストの場合や、それらの出現数等など応用方法はオブジェクトリストからキーとその出現数を抽出するを参照


select で出力対象を絞る

例 : リストから mail が空じゃないレコードだけ処理する。

$ cat hoge.json | jq -r '.[] | select( .email != "" ) | [ .name , .email ] | @tsv'

データによっては select 部分は select( .email != null ) の方が良い結果になるかもしれない。


key - value 構造の key

ユニークであることが自明なリストとして連想配列を使う場合以下の様なしばしばデータ構造になる。

{
	"foo" : {
				"url" : "www.foo.com",
				"email" : "foo@foo.com"
			},
	"bar" : {
				"url" : "www.bar.com",
				"email" : "bar@bar.com"
			}
	...
}


こういった場合 jqjq '.[]' で バリュー(.url,.email)を舐めるのは簡単だがその場合に key が取り出せない。

このような場合、組み込み関数の to_entries[{"key" : "foo" ,"value" : {..}},...] という形に一旦トランスフォームしてから処理すれば良い。

$ cat user_sites.json | jq -r '. | to_entries | .[] | [ .key , .value.email ] | @csv'
"foo","foo@foo.com"
"bar","bar@bar.com"

オブジェクトリストからキーとその出現数を抽出する

$ jq -r '[.[]|keys]|flatten|group_by(.) | map({(.[0]): length}) | add'

例 :

[
	{
		"x" : "hoge",
		"y" : "moge",
		"z" : "zoge"
	},
	{
		"x" : "ほげ",
		"z" : "ぞげ"
	}
]

から

{
  "x": 2,
  "y": 1,
  "z": 2
}

というオブジェクトを取得する

キーのユニークリストを取得するだけなら

$ jq -r '[.[]|keys]|flatten|unique|sort'

で OK

解説(出現数まで出すケース)

1. keys でキーで構成されるリストを取得する

$ echo '[{"x":"hoge","y":"moge","z":"zoge"},{"x":"ほげ","z":"ぞげ"}]' | jq -r '.[]|keys'
[
  "x",
  "y",
  "z"
]
[
  "x",
  "z"
]

2. 上記コマンドを[] でくくることで JSON として再処理可能にする

$ echo '[{"x":"hoge","y":"moge","z":"zoge"},{"x":"ほげ","z":"ぞげ"}]' | jq -r '[.[]|keys]'
[
  [
    "x",
    "y",
    "z"
  ],
  [
    "x",
    "z"
  ]
]

3. flattenで一次配列化する

echo '[["x","y","z"],["x","z"]]' | jq -r '. | flatten'
[
  "x",
  "y",
  "z",
  "x",
  "z"
]

4. group_by でグルーピングする

$ echo '["x","y","z","x","z"]' | jq '. | group_by(.)'
[
  [
    "x",
    "x"
  ],
  [
    "y"
  ],
  [
    "z",
    "z"
  ]
]

5. map で各リスト毎にその 1 アイテム目を key にして配列長を value に変形する

$ echo '[["x","x"],["y"],["z","z"]]' | jq '. | map({(.[0]):length})'
[
  {
    "x": 2
  },
  {
    "y": 1
  },
  {
    "z": 2
  }
]

6. add で一つのオブジェクトにまとめる

$ echo '[{"x":2},{"y":1},{"z":2}]' | jq '. | add'
{
  "x": 2,
  "y": 1,
  "z": 2
}

オブジェクトリストから一つのキーバリューを取り出す

やりたいこと

{
	"family_name": "foo-bar",
	"family":
	[
	    {
	        "name": "hoge",
	        "age": 10
	    },
	    {
	        "name": "moge",
	        "age": 11
	    }
	]
}

という感じのデータからトップレベル family_name の値と、 moge さんの年齢を一発で取り出したい

解答

$ echo '{"family_name": "foo-bar","family":[{"name": "hoge","age": 10},{"name": "moge","age": 11}]}' | jq '[ .family_name , (.family[] | select( .name == "moge" ) | .age) ]'
[
  "foo-bar",
  11
]

注意事項

jq の引数にはシングルクオートを使う

jq に渡す文字列をダブルクオートで渡すと、bash[(test) や select などが解釈されてしまうため

jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?) at <top-level>, line 1:
.family[] | select( .name == 'moge' ) | .age                             
jq: 1 compile error

という感じのエラーになるので注意。


オブジェクトリストの値から新たなオブジェクトを作る

オブジェクトリストからキーとその出現数を抽出する

に似ているが、やりたいことは:

[
	{
		name: "hoge",
		age: 10
	},
	{
		name: "moge",
		age: 11
	}
]

という感じのオブジェクトリストから

{
	"hoge": 10
	"moge": 11
}

という感じのハッシュを作る。

ちょっと分からなかったので google 先生に聞いて見つけた。

$ jq 'map( {(.name): .age} ) | add'

いまいち mapadd の挙動が分からない。

いや map はなんとなく分かるが、その出力と add の作用が良く分からないと行った方が正確か。

mapperl 同様、リストの要素に対しで処理を行う。
map_values を使うと、ハッシュの値に対して処理を行うことが出来る。

今回の場合、リストの要素であるオブジェクトを {(.name): .age} に置き換えてると読むことが出来るが、その後の add が分からない。

実際に add なしで実行した結果は以下の通りだ。

$ jq 'map( {(.name): .age} )' data.json 
[
  {
    "hoge": 10
  },
  {
    "moge": 11
  }
]

公式の add のサンプルは以下の様なものである

    jq 'add'
Input	["a","b","c"]
Output 	"abc"

リファレンスチョー訳抜粋

入力配列の要素のタイプに応じて、足し算や文字列結合、マージを意味する。

となると、わざわざ map を使わなくても出来るはず

jq '[.[]|{(.name): .age}] | add' data.json
{
  "hoge": 10,
  "moge": 11
}

できた


複数の JSON ファイルを一つのリストに格納する - -s オプション

  • -s,--slurp
    入力のJSONオブジェクトごとにフィルターを実行するのではなく、入力ストリーム全体を大きな配列に読み込んで、一度だけフィルターを実行します。
$ jq -s '.' file1 file2 ...

AWS CloudTrail のようにファイル名に日時が付与された JSON ファイル群を join したいなら ls + sort 等を利用する。

$ jq -s '.' $(ls *.json | sort)
$ jq -s '[.[]|keys]|flatten|unique' $(find extracted-json -name "*.json" -not -name "invalid-*")
[
  "_access_log_attr",
  "hardware",
  "salt",
  "unity",
  "user"
]

AWS EC2 のタグ情報からパブリック DNS 名を取得 - AWS CLI

タグ UniqueName の値が Tags value であるインスタンスのパブリック DNS 名を取得する

$ aws ec2 describe-instances --filters '{"Name":"tag:UniqueName","Values": ["Tags value"]}' | jq -r '.Reservations[0].Instances[0].PublicDnsName'

特定日以降で Name タグのあるスナップショットのリストを得る - AWS CLI

aws ec2 describe-snapshots --query "Snapshots[?(StartTime>='2021-03-13')]" | jq -r '.[] | select( .Tags != null ) | select( (.Tags[] | select( .Key == "Name" ))) | [ (.Tags[] | select( .Key == "Name" ) | .Value ) , .SnapshotId , .StartTime , .Description ] | @tsv' | sort

要点解説

  • --query "Snapshots[?(StartTime>='2021-03-13')]"
    • スナップショットの作成開始時刻 = StartTime が 2021-03-13(の 00:00:00)以降のスナップショットを取り出す

AWS CLI の返り値は各スナップショットの情報を格納した dict のリスト

  • .[] | select( .Tags != null )

    • リストからタグが空では無い要素を取り出す(暗黙的にスナップショットが取られているものが多数あるっぽく、それらには Tags が無い。このためそれらについてタグ内の要素を参照すると null のアトリビュートを参照する式になってしまい jq が死ぬのでフィルタリング)
  • | select( (.Tags[] | select( .Key == "Name" )))

    • タグのキーが Name のアイテムがあるものを取り出す
    • タグ(Tags[])がるので jq が死なない
  • | [ (.Tags[] | select( .Key == "Name" ) | .Value ) , .SnapshotId , .StartTime , .Description ]

    • 欲しいアトリビュートを列挙した配列を作る
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?