
JSONデータの操作にjqコマンド使ってみたら扱いやすかったので紹介したい
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
前回の記事で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をインストールする前にどんな感じで抽出されるか確認したい場合は、以下のページでも確認できます。ただ、入力したデータの取り扱いに関する記載が見つけられなかったので、データをマスキングしてお試しください。














