JSONデータの操作にjqコマンド使ってみたら扱いやすかったので紹介したい
はじめに
前回の記事でAWS CLIコマンドのJSONデータをjqコマンドで操作したら大変便利だったので紹介します。
jqコマンドとは
JSONデータをsedやgrep、awkのようにデータ抽出、変換、集計してくれるツールです。
AWS CLIのの出力形式は、複数の出力形式がサポートされていますが、デフォルトではJSONで出力されます。
AWS CLIでも--filters
や--query
を使えば抽出や変換できますが、AWS CLI以外のJSONデータ(公開されている様々なAPIが、ほぼJSONで出力)を操作するとき、jqコマンドのようなツールを覚えておくと便利かなと考えました。
公開API(例)
使い方
jqは各種OSにインストールして利用できます。今回はmacOS Catalinaにインストールした環境で操作していきます。
バージョン確認コマンドが出力されればOKです。
% jq --version jq-1.6
jqで操作するJSONデータは、aws ec2 describe-vpcs
で出力されたデータをローカルに保存しておきます。AWS CLIコマンドをパイプで渡して操作もできるので都度ローカルに保存する必要はありません。
describe-instances.json
{ "Vpcs": [ { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }, { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } ] }
シンプルな出力
まずはシンプルにjqコマンドを実行してみます。
% cat ./describe-vpcs.json | \ jq '.' { "Vpcs": [ { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }, { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } ] }
catコマンドと出力結果は変わりませんが、厳密には標準出力された内容をJSONで標準出力しています。引数'.'
が、JSONオブジェクト(ブレース{
で始まりブレース}
で終わる)を表しています。
配列を指定した出力
インデックス(0から始まる)を指定して抽出できます。describe-vpcs.jsonには、VPcsに2つの配列(0,1)があるので、ブラケット[]
に[0]
を指定して抽出してみます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[0]' { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }
指定したインデックスにデータが存在しない場合は、Nullが返されます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[2]' null
範囲を指定した出力
範囲を指定して配列を抽出できます。
ブラケット[]
を[0:2]
では、0以上2未満で抽出されます。範囲の開始を指定しない[:2]
の場合は、0から開始されるので先程とおなじく0以上2未満で抽出されます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[0:2]' [ { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }, { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } ]
% cat ./describe-vpcs.json | \ jq '.Vpcs[:2]' [ { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }, { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } ]
後方から範囲指定する場合は[-1:]
として抽出することもできます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[-1:]' [ { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } ]
なお、範囲指定でインデックスにデータがない場合は何も表示されず、nullが返されません。
最後尾から出力
配列のインデックスをreverseして抽出できます。範囲を指定した出力と一緒でreverse[]
のブラケットにインデックスや範囲を指定して抽出できます。
% cat ./describe-vpcs.json | \ jq '.[] | reverse[]' { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-87e9dxxx", "State": "available", "VpcId": "vpc-354f2873de5162xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e99eaeae46622xxx", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false } { "CidrBlock": "10.255.0.0/16", "DhcpOptionsId": "dopt-77e9dxxx", "State": "available", "VpcId": "vpc-029f2873de5756xxx", "OwnerId": "xxxxxxxxxxxx", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0e18eaeae48822xxx", "CidrBlock": "10.255.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false }
値の出力
各配列にある値を抽出する場合は、JSONデータ構造を意識して抽出します。VpcsのCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[].CidrBlock' "10.255.0.0/16" "10.0.0.0/16"
インデックスを指定しなければ、すべての配列からCidrBlock値のValueが抽出されます。
また、ネストされた配列からも抽出できます。CidrBlockAssociationSetのCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[].CidrBlockAssociationSet[].CidrBlock' "10.255.0.0/16" "10.0.0.0/16"
値の出力(フィルタリング)
select
jqには、様々な組み込み演算子や関数が用意されています。先程の値の抽出で特定の値だけ抽出する場合は、selectを使ってフィルタリングできます。
CidrBlock値が10.255.0.0/16
と一致する配列からCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock == "10.255.0.0/16") | .CidrBlock ' "10.255.0.0/16"
contains
CidrBlock値に16
を含む配列からCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| contains ("16")) | .CidrBlock ' "10.255.0.0/16" "10.0.0.0/16"
startswith
10.255
から始まるCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| startswith ("10.255")) | .CidrBlock '
endswith
16
で終わるCidrBlock値を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| endswith ("16")) | .CidrBlock '
AND OR
ANDやOR条件も使えます。10.255
から始まる且つ、16
で終わる配列を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| startswith ("10.255") and endswith ("16")) | .CidrBlock ' "10.255.0.0/16"
10.255
から始まるまたは16
で終わる配列を抽出します。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| startswith ("10.255") or endswith ("16")) | .CidrBlock ' "10.255.0.0/16" "10.0.0.0/16"
出力結果を整形
フィルタリングした内容を整形することができます。先程の出力だと値だけ出力されるので{ 任意キー(String): .CidrBlock }
を追加して出力できます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| startswith ("10.255") or endswith ("16")) | { CidrBlock: .CidrBlock } ' { "CidrBlock": "10.255.0.0/16" } { "CidrBlock": "10.0.0.0/16" }
VpcIdも一緒に出力してみます。カンマ,
で区切ればいくつも追加して出力できます。
% cat ./describe-vpcs.json | \ jq '.Vpcs[] | select( .CidrBlock| startswith ("10.255") or endswith ("16")) | { CidrBlock: .CidrBlock, VpcId: .VpcId} ' { "CidrBlock": "10.255.0.0/16", "VpcId": "vpc-029f2873de5756xxx" } { "CidrBlock": "10.0.0.0/16", "VpcId": "vpc-354f2873de5162xxx" }
RAWデータで出力
jqはJSON形式で出力するので抽出された最小の値でもダブルクォーテーションで括られたデータで出力されます。jqコマンドにオプション-r
を追加するとRAWデータで出力されます。
% cat ./describe-vpcs.json | \ jq -r '.Vpcs[] | select( .CidrBlock| startswith ("10.255") and endswith ("16")) | .CidrBlock ' 10.255.0.0/16
さいごに
JSONデータを良い感じに操作できるjqコマンドの紹介でした。基本的な一部の出力方法をご案内しましたが、まだまだいろんな機能があります。詳しくは公式のManualを参照してください。
jqをインストールする前にどんな感じで抽出されるか確認したい場合は、以下のページでも確認できます。ただ、入力したデータの取り扱いに関する記載が見つけられなかったので、データをマスキングしてお試しください。