jq: Как отобразить значение, только если его ключ существует?

#json #key #jq

#json #Клавиша #jq

Вопрос:

У меня есть следующий json:

 {
    "SecurityGroups": [
        {
            "Description": "SG for ssh-proxy server",
            "GroupName": "ssh-proxy-SG",
            "IpPermissions": [
                {
                    "FromPort": 161,
                    "IpProtocol": "udp",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 161,
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-22e04e44",
                            "UserId": "XXXXXXXXXXXX"
                        }
                    ]
                },
                {
                    "FromPort": 22,
                    "IpProtocol": "tcp",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 22,
                    "UserIdGroupPairs": []
                },
                {
                    "FromPort": -1,
                    "IpProtocol": "icmp",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": -1,
                    "UserIdGroupPairs": []
                }
            ],
            "OwnerId": "XXXXXXXXXXXX",
            "GroupId": "sg-4f1d8a35",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "Tags": [
                {
                    "Key": "Name",
                    "Value": "ssh-proxy-SG"
                }
            ],
            "VpcId": "vpc-d3131fbe"
        }
    ]
}
  

Используя jq , я пытаюсь отобразить такой список:

 161 tcp sg-22e04e44
22 tcp 0.0.0.0/0
-1 icmp 0.0.0.0/0
  

Я могу напечатать только порт и протокол, но не «.IpRanges[].CidrIp», вот так:

 ✗ aws ec2 describe-security-groups --group-id sg-4f1d8a35 --profile XXXX --region us-east-1 --output json | jq -r '.SecurityGroups[].IpPermissions[] | (.FromPort|tostring)   " "   .IpProtocol'
161 udp
22 tcp
-1 icmp
  

Проблема в том, что иногда (в данном случае в правиле 161 udp) отсутствует «.IpRanges[].CidrIp» и это вызывает ошибку.

Я пробовал это:

 ✗ aws ec2 describe-security-groups --group-id sg-4f1d8a35 --profile XXXXXXX --region us-east-1 --output json | jq -r '.SecurityGroups[].IpPermissions[] | (.FromPort|tostring)   " "   .IpProtocol   " "   .IpRanges[].CidrIp'

22 tcp 0.0.0.0/0
-1 icmp 0.0.0.0/0
  

Но, как вы можете видеть, строка udp отсутствует.

Также пробовал это:

 ✗ aws ec2 describe-security-groups --group-id sg-4f1d8a35 --profile XXXXXXX --region us-east-1 --output json | jq -r '.SecurityGroups[].IpPermissions[] | .IpRanges[]? |= if has(.CidrIp) then (.FromPort|tostring)   " "   .IpProtocol   " "   .IpRanges[].CidrIp else (.FromPort|tostring)   " "   .IpProtocol   " "   .UserIdGroupPairs[].GroupId end'

{
  "FromPort": 161,
  "IpProtocol": "udp",
  "IpRanges": [],
  "Ipv6Ranges": [],
  "PrefixListIds": [],
  "ToPort": 161,
  "UserIdGroupPairs": [
    {
      "GroupId": "sg-22e04e44",
      "UserId": "XXXXXXX"
    }
  ]
}
jq: error (at <stdin>:72): Cannot iterate over null (null)
  

Что я делаю не так?

Комментарии:

1. Вы заметили, что CidrIP отсутствует для udp записи

2. Да, забыл упомянуть об этом, именно в этом моя проблема

3. Вам нужна пустая строка для этого? Как должен выглядеть ваш вывод?

4. 161 tcp sg-22e04e44 22 tcp 0.0.0.0/0 -1 icmp 0.0.0.0/0

5. Ах, я думал, что // это комментарий, и не включил его в свою команду, он работает отлично. Извините за недоразумение, не могли бы вы, пожалуйста, вернуть свой ответ?

Ответ №1:

CidrIP Отсутствует для udp записи. Если вы хотите чередовать .GroupId и CidrIp , вы можете выполнить чередование следующим образом

 .SecurityGroups[].IpPermissions[] |
.FromPort as $port          |
.IpProtocol as $prot        |
.IpRanges as $ip            |
.UserIdGroupPairs as $group |
[
  ($port|tostring),
  ($prot),
  (select($ip|length) | $ip[].CidrIp) //
  (select($group|length) | $group[].GroupId) //
  empty
] |
@tsv
  

выдает результат, подобный тому, который вы хотели

 161 udp sg-22e04e44
22  tcp 0.0.0.0/0
-1  icmp    0.0.0.0/0
  

Вы можете использовать --raw-output/-r режим вместе с любым из @csv , @tsv или join(" ") для представления выходных данных в табличной форме.

jqplay — онлайн демо-версия

Комментарии:

1. Это не то, что я пытаюсь сделать, в правиле udp я пытаюсь отобразить «.UserIdGroupPairs. Идентификатор группы » if «.IpRanges[]» не содержит «.CidrIp» .

2. Смотрите правки. Показанный код выдает именно такой результат

3. Предупреждение: Проверка того, существует ключ или нет, может быть выполнена непосредственно с помощью has/1 , но не // в одиночку. Причина в том, что если FOO вычисляется до единственного значения, скажем, $ foo, то FOO // BAR вычисляется до BAR, если $ foo равно false или null.

4. @peak: надеюсь, теперь лучше

5. Хм. jq -n '([],{},0,null) | select(length)' выдает: [] {} 0 null