シェルでのJSON解析

シェルでのJSON解析

シェルで JSON 出力を解析するにはどうすればよいですか?

たとえば、Amazon Web Services では、インスタンスのステータスを取得するための CLI が提供されています。

$ aws ec2 describe-instances <my_instance_id>

ただし、コマンドは JSON 文字列を返します。そのコマンドの出力は次のようになります。

$ aws ec2 describe-instances x12345
{
    "Reservations" :
     {  
            "OwnerId": "1345345"
            "Groups": [], 
            "SecurityGroups": [
               {
                  "Foo" : "yes"
                  "Bar" : "no
               }
             ]
     }
}

JSON 出力を解析するために使用できるシェルの組み込み機能はありますか?

たとえば、FOO次のものをシェル変数にキャプチャしたいとしますoutput["Reservations"]["SecurityGroups"][0]{"Foo"}

役に立つかもしれないが、私は Zsh から機能するソリューションに特に興味がある。

答え1

私が理解している限りでは、あなたは「Foo」の値を探しているのでしょう。これは本当にシェルのコマンドラインツールを使用すると簡単に実行できます。これは、独自の種類のパーサー言語を実装する点で にjq似ています。例を挙げると、sed

json='
{
    "Reservations" :
     {  
            "OwnerId" : "1345345",
            "Groups" :  [],
            "SecurityGroups" : [
               {
                  "Foo" : "yes",
                  "Bar" : "no"
               }
             ]
     }
}'

jq次のように簡単に取得できますyes

printf %s "$json" |
jq '.[].SecurityGroups[0].Foo?'                                                

出力

"yes"

表記法を使用してオブジェクト ハッシュまたは辞書リストを調べることができます.dot。また、インデックス付き配列は、おそらくご想像のとおり、数値の角括弧付きインデックスを使用して、より簡単にインデックス付けできます。上記のコマンドでは、空のインデックス形式を使用して、そのレベルの反復可能な項目をすべて展開することを示しています。次のようにすると理解しやすいかもしれません。

printf %s "$json" | jq '.[][]'

... これにより、ハッシュ内の第 2 レベルの項目のすべての値が抽出され、次のようになります...

"1345345"
[]
[
  {
    "Foo": "yes",
    "Bar": "no"
  }
]

これは の機能のほんの一部にすぎませんjq。これはシェルでデータをシリアル化するための非常に強力なツールであり、古典的な Unix スタイルで単一の実行可能バイナリにコンパイルされ、ディストリビューションのパッケージ マネージャー経由で利用できる可能性が高く、非常によく文書化されています。git-ページそして自分の目で確かめてください。

ちなみに、階層化されたデータに取り組む別の方法はjson、少なくとも何を扱っているかを知るために、逆に表記法を使って.dot分割する方法です。全て全て次のようなレベル:

printf %s "$json" | jq '..'

{
  "Reservations": {
    "OwnerId": "1345345",
    "Groups": [],
    "SecurityGroups": [
      {
        "Foo": "yes",
        "Bar": "no"
      }
    ]
  }
}
{
  "OwnerId": "1345345",
  "Groups": [],
  "SecurityGroups": [
    {
      "Foo": "yes",
      "Bar": "no"
    }
  ]
}
"1345345"
[]
[
  {
    "Foo": "yes",
    "Bar": "no"
  }
]
{
  "Foo": "yes",
  "Bar": "no"
}
"yes"
"no"

jqしかし、おそらく、さまざまな種類のノードに対して提供される多くの検出または検索方法の 1 つを使用する方がはるかに良いでしょう。

答え2

これはあなたの目的に対する答えですが、質問に対する答えではありません。つまり、JSON パーサーを使用せずに目標を達成できるということです。

AWS CLIユーティリティには、引数を使用して選択したフィールドのみを出力する機能があります--query。これは文書化されていますここ

例えば:

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupName' \
  --instance-id i-6b272337 \
  --output text
mongodb

必要に応じて複数のフィールドを選択することもできます。

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[0].[GroupName,GroupId]' \
  --instance-id i-6b272337 \
  --output text
mongodb sg-710ffa14

また、複数の一致する構造体を表示することもできます。

$ aws ec2 describe-instances \
  --query 'Reservations[0].Instances[0].SecurityGroups[*].[GroupName,GroupId]' \
  --instance-id i-6b272337 \
  --output text
mongodb sg-710ffa14
default sg-a0243bcc

関連情報