jq로 조금 복잡한 검색하기

15295 단어 jqAWS
jq 명령으로 배열의 요소이고 복잡한 구조를 가진 요소(여기서는 aws ec2 describe-instances의 결과)에 대해 검색을 해 보았습니다.

소개



㈜ 주식회사 아이리지의 Common Lisp 사랑하는 서버사이드 엔지니어, tanaka.lisp입니다.

AWS EC2를 사용하는 경우 특정 인스턴스의 정보(인스턴스 ID, 인스턴스 유형 등)를 알고 싶어질 수 있습니다. 다만, 그 때문에만 관리 콘솔을 포치포치하고 취득하고 있으면, 왠지 졌던 생각이 들고(이것 소중히), 쉘 스크립트등으로 결과를 이용할 때에 불편합니다.
awscliaws ec2 describe-instances 에서 얻은 결과를 jq 에 먹여 인스턴스에 붙이고 있는 이름에서 좋게 검색하고 성형하고 싶은 곳이지만, awscli :
{
  "Reservations": [
    "Instances": [
      {
        "InstanceId": "i-commonlisp",
        "Tags": [
          {
            "Key": "Name",
            "Value": "app.commonlisp"
          },
          {
            "Key": "aws.cloudformation:stack-name",
            "Value": "stack.commonlisp"
          }
        ],
        ...
      },
      ...
    ],
    ...
  ]
}

여기서 이름( TagsKeyName 의 값)으로 인스턴스를 검색하는 방법을 시행착오했습니다.

준비하는 것


  • jq : jq-1.5-1-a5b5cbe
  • awscli : aws-cli/1.11.58 Python/3.6.0 Linux/4.8.0-41-generic botocore/1.5.21

  • jq에서 검색


    jq 에서는 입력의 JSON에 대해, 다음과 같이 할 수 있습니다:
  • 입력 JSON 성형 ( {"id": .InstanceId, "tags": .Tags} )
  • 입력 JSON에서 데이터 추출 ( select )

  • 이번에는 이것들을 조합해, aws ec2 describe-instances 의 결과로부터 찾고 있는 인스턴스의 인스턴스 ID( InstanceId )를 취득해 보겠습니다.

    입력 성형



    예를 들어 이런 JSON 객체가 있고,
    [
      {
        "name": "Lisp",
        "developer": "John McCarthy"
      },
      {
        "name": "Common Lisp",
        "developer": "ANSI X3J13 committee",
        "spec": "ANSI INCITS 226-1994 (R2004)"
      },
      {
        "name": "Arc",
        "developer": "Paul Graham",
        "books": [
          "On Lisp",
          "Hackers and Painters"
        ]
      },
      {
        "name": "Clojure",
        "developer": "Rich Hicky"
      }
    ]
    

    언어명( lang )과 개발자명( developer )만을 원할 때 입력을 printf 적으로 성형할 수 있습니다.
    $ echo '...上のJSON...' \
       | jq '.[] | {"lang": .name, "developer": .developer}'
    {
      "lang": "Lisp",
      "developer": "John McCarthy"
    }
    {
      "lang": "Common Lisp",
      "developer": "ANSI X3J13 committee"
    }
    {
      "lang": "Arc",
      "developer": "Paul Graham"
    }
    {
      "lang": "Clojure",
      "developer": "Rich Hicky"
    }
    

    요소를 추출하는 것처럼 보이지만, 별도로 더 추출한 오퍼레이터가 있으므로, 이쪽은 整形 라고 불렀습니다.

    입력에서 데이터 추출



    조건을 만족하는 것만을 pass through 한다고 하는 오퍼레이터도 있어, 그것이 select 입니다. 여기를 抽出 라고 여기에서는 부르고 있습니다.
    man 에 있는 것이 전부입니다만,
       select(boolean_expression)
           The function select(foo) produces its input unchanged if foo returns true for that input, and produces no output otherwise.
    
           It´s useful for filtering lists: [1,2,3] | map(select(. >= 2)) will give you [2,3].
    

    정규식을 조합할 수 있으므로 강력합니다;
    $ echo '...上のJSON...' \
      | jq '.[] | .name |select( .| test("Lisp"))'
    "Lisp"
    "Common Lisp"
    

    실제로 추출



    Exactly 이름으로 인스턴스 찾기



     에서는 실제로 검색 쿼리를 만들어 보겠습니다.

    우선 인스턴스 리스트를 얻고,
    $ aws ec2 describe-instances | jq '.Reservations[].Instances[]'
    {
      "ImageId": "ami-commonlisp",
      "State": {
        "Code": 16,
        "Name": "running"
      }
    }
    {
    ...  # ずらずら出るので省略
    

    거기에 TagsKeyNameValue 가 Exactly app.commonlisp 의 요소만 참이 되는 조건을 더합니다 ( 루시의 파르시가 퍼지로 코쿤 느낌).
    $ aws ec2 describe-instances | jq '.Reservations[].Instances[]
      | select(.Tags[].Key == "Name" and .Tags[].Value == "app.commonlisp")
    {
      "InstanceId": "i-commonlisp",
      ...  # ずらずら出るので省略
    }
    

    그런 다음 불필요한 요소는 나오지 않도록합시다.
    $ aws ec2 describe-instances | jq '.Reservations[].Instances[]
      | select(.Tags[].Key == "Name" and .Tags[].Value == "app.commonlisp")
      | {"instance-id": .InstanceId, "tags": .Tags}'
    {
      "instance-id": "i-commonlisp",
      "tags": [
        {
          "Key": "Name",
          "Value": "app.commonlisp"
        },
        {
          "Key": "aws:autoscaling:groupName",
          "Value": "commonlisp-ApplicationFleet"
        },
        ...  # タグが出るので省略
      ]
    }
    

    이름 비밀 인스턴스 검색



    왠지 app 라고 이름을 붙인 것 같은 생각이 들지만, 전혀 생각나지 않는다. 우리는 분위기에서 서버를 세우고 있습니다. . 그런 당신에게 바친다. 주로 나 자신에게 바치는 .
    $ aws ec2 describe-instances | jq '.Reservations[].Instances[]
      | select( .Tags[].Key == "Name" and (.Tags[].Value | test("^app")))
      | {"instance-id": .InstanceId, "tags": .Tags}'
    {
      "instance-id": "i-commonlisp",
      "tags": [
        {
          "Key": "Name",
          "Value": "app.commonlisp"
        },
        {
          "Key": "aws:autoscaling:groupName",
          "Value": "commonlisp-ApplicationFleet"
        },
        ...  # タグが出るので省略
      ]
    }
    {
      "instance-id": "i-clojure",
      "tags": [
        {
          "Key": "Name",
          "Value": "app.clojure"
        },
        {
          "Key": "aws:autoscaling:groupName",
          "Value": "clojure-ApplicationFleet"
        },
        ...  # タグが出るので省略
      ]
    }
    

    결론


    jq 에서의 검색에 대해 다음을 언급했습니다.
  • 요소를 검색하는 기본 방법
  • 대상 요소의 추가 사전을 검색 조건으로 만드는 방법
  • jq 라고 잘 생긴 DSL군요. jq 는 튜링 완전 그렇지만 것 같고, 바로 JSON 시대의 awk 감이 있습니다.

    그런데 jq에서 검색 할 뿐인데 왜 기사가 길어지는지

    좋은 웹페이지 즐겨찾기