Я пытаюсь извлечь диапазоны из приведенного ниже вывода JSON,
{
"zscaler.net": {
"continent : EMEA": {
"city : Amsterdam II": [
{
"range": "165.225.240.0/23",
"vpn": "ams2-2-vpn.zscaler.net",
"gre": "165.225.240.12",
"hostname": "ams2-2.sme.zscaler.net",
"latitude": "52",
"longitude": "5"
},
{
"range": "147.161.132.0/23",
"vpn": "",
"gre": "",
"hostname": "",
"latitude": "52",
"longitude": "5"
}
],
"city : Brussels II": [
{
"range": "147.161.156.0/23",
"vpn": "",
"gre": "",
"hostname": "",
"latitude": "50",
"longitude": "5"
}
]
},
"continent : Americas": {
"city : Atlanta II": [
{
"range": "104.129.204.0/23",
"vpn": "atl2-vpn.zscaler.net",
"gre": "104.129.204.32",
"hostname": "atl2.sme.zscaler.net",
"latitude": "34",
"longitude": "-84"
},
{
"range": "136.226.2.0/23",
"vpn": "",
"gre": "104.129.204.32",
"hostname": "",
"latitude": "34",
"longitude": "-84"
}
]
}
}
}
Который я успешно могу извлечь с помощью следующей команды:
.["zscaler.net"]["continent : EMEA"] | .[][].range
Я ищу, чтобы попытаться получить родительский континент и родительский город каждого диапазона рядом друг с другом, как через запятую,
165.225.240.0/23, EMEA, Amsterdam II
147.161.132.0/23, EMEA, Amsterdam II
147.161.156.0/23, EMEA, Brussels II
Я пытаюсь добиться этого полностью в jq и bash.
Пожалуйста помоги.
Требование было отредактировано, извините за это. Но я хочу получить родительские значения континента и города рядом с каждым из диапазонов.
Не уверен, что этот комментарий ответил на мой вопрос. Вам нужен вышеуказанный вывод для американского региона или нет?
Да, это будет для всех регионов
Вы можете использовать to_entries
в сочетании с sub() для очистки ключа, затем использовать join(",") (или @csv), чтобы получить желаемый результат:
.["zscaler.net"]["continent : EMEA"]
| to_entries[]
| .key as $k
| .value[]
| [ .range, ($k | sub("city : "; "" )) ] | join(", ")
.["zscaler.net"]["continent : EMEA"]
.key as $k
.value
.range
текущего объекта$k
), но мы используем sub, чтобы заменить city :
ничем (""
)"165.225.240.0/23, Amsterdam II"
"147.161.132.0/23, Amsterdam II"
"147.161.156.0/23, Brussels II"
Если вам нужен вывод для каждого континента, удалите жестко запрограммированный ["continent : EMEA"]
, его можно заменить другим []
, чтобы перебрать их и получить следующий вывод:
.["zscaler.net"][] | to_entries[] | .key as $k | .value[] | [ .range, ($k | sub("city : "; "" )) ] | join(", ")
165.225.240.0/23, Amsterdam II
147.161.132.0/23, Amsterdam II
147.161.156.0/23, Brussels II
104.129.204.0/23, Atlanta II
136.226.2.0/23, Atlanta II
Я считаю, что это то же самое, что и мой вывод? Пожалуйста, добавьте желаемый результат к вашему вопросу
165.225.240.0/23, EMEA, Амстердам II
Кроме того, мне бы очень хотелось краткое объяснение вашей команды. Я все еще учусь jq, изо всех сил стараюсь подняться туда.
Я добавил более подробное объяснение
Кроме того, FYI - OP хочет, чтобы это было для всех регионов - stackoverflow.com/questions/75983859/…
Добавлены пояснения по замене ["continent : EMEA"]
другим []
для перебора нескольких регионов.
Что-то не так с выводом/объяснением @deadshotpro?
@0stone0 Ваш ответ действительно помог. Это был не тот результат, который я искал. Тем не менее, мне удалось состряпать что-то свое, прослушав вашу команду и объяснение. Огромное спасибо за это!
Вы можете комбинировать нужные поля вот так \(.first) \(.second)
:
jq -r '.["zscaler.net"]["continent : EMEA"] | .[][]|"\(.range), \(.latitude), \(.longitude)"'
Выход:
165.225.240.0/23, 52, 5
147.161.132.0/23, 52, 5
147.161.156.0/23, 50, 5
Спасибо, это полезно и информативно. Прямо сейчас в требование внесено редактирование, извините за это. Но я хочу получить родительские значения континента и города рядом с каждым из диапазонов.
Вот решение, которое соответствует желаемому результату вопроса:
.["zscaler.net"] | to_entries[]
| (.key|ltrimstr("continent : ")) as $cont
| .value | to_entries[]
| "\(.value[].range), \($cont), \(.key|ltrimstr("city : "))"
Выход:
165.225.240.0/23, EMEA, Amsterdam II
147.161.132.0/23, EMEA, Amsterdam II
147.161.156.0/23, EMEA, Brussels II
104.129.204.0/23, Americas, Atlanta II
136.226.2.0/23, Americas, Atlanta II
Вот еще один способ сделать это с помощью tostream
.
На выходе tostream
будут все родители и ключ для каждого значения. Этот поток может быть отфильтрован (select
) по ключу range
, а затем нужные части элемента потока могут быть извлечены/отформатированы/выведены.
.["zscaler.net"] | tostream
| select(.[0][-1] == "range")
| [.[-1], .[0][0:2][]|split(":")[-1]]
| join(",")
Пример вывода:
165.225.240.0/23, EMEA, Amsterdam II
147.161.132.0/23, EMEA, Amsterdam II
147.161.156.0/23, EMEA, Brussels II
104.129.204.0/23, Americas, Atlanta II
136.226.2.0/23, Americas, Atlanta II
Попробуйте на jqplay.org.
Вдохновением для этого решения было признание того, что все желаемые поля вывода содержатся в одном элементе вывода потока. Понять конвейер проще всего, увидев, что происходит после каждого этапа.
Фильтр:
.["zscaler.net"] | tostream
[["continent : EMEA","city : Amsterdam II",0,"range"],"165.225.240.0/23"]
[["continent : EMEA","city : Amsterdam II",0,"vpn"],"ams2-2-vpn.zscaler.net"]
[["continent : EMEA","city : Amsterdam II",0,"gre"],"165.225.240.12"]
[["continent : EMEA","city : Amsterdam II",0,"hostname"],"ams2-2.sme.zscaler.net"]
[["continent : EMEA","city : Amsterdam II",0,"latitude"],"52"]
[["continent : EMEA","city : Amsterdam II",0,"longitude"],"5"]
[["continent : EMEA","city : Amsterdam II",0,"longitude"]]
[["continent : EMEA","city : Amsterdam II",1,"range"],"147.161.132.0/23"]
[["continent : EMEA","city : Amsterdam II",1,"vpn"],""]
[["continent : EMEA","city : Amsterdam II",1,"gre"],""]
[["continent : EMEA","city : Amsterdam II",1,"hostname"],""]
[["continent : EMEA","city : Amsterdam II",1,"latitude"],"52"]
[["continent : EMEA","city : Amsterdam II",1,"longitude"],"5"]
[["continent : EMEA","city : Amsterdam II",1,"longitude"]]
[["continent : EMEA","city : Amsterdam II",1]]
[["continent : EMEA","city : Brussels II",0,"range"],"147.161.156.0/23"]
[["continent : EMEA","city : Brussels II",0,"vpn"],""]
[["continent : EMEA","city : Brussels II",0,"gre"],""]
[["continent : EMEA","city : Brussels II",0,"hostname"],""]
[["continent : EMEA","city : Brussels II",0,"latitude"],"50"]
[["continent : EMEA","city : Brussels II",0,"longitude"],"5"]
[["continent : EMEA","city : Brussels II",0,"longitude"]]
[["continent : EMEA","city : Brussels II",0]]
[["continent : EMEA","city : Brussels II"]]
[["continent : Americas","city : Atlanta II",0,"range"],"104.129.204.0/23"]
[["continent : Americas","city : Atlanta II",0,"vpn"],"atl2-vpn.zscaler.net"]
[["continent : Americas","city : Atlanta II",0,"gre"],"104.129.204.32"]
[["continent : Americas","city : Atlanta II",0,"hostname"],"atl2.sme.zscaler.net"]
[["continent : Americas","city : Atlanta II",0,"latitude"],"34"]
[["continent : Americas","city : Atlanta II",0,"longitude"],"-84"]
[["continent : Americas","city : Atlanta II",0,"longitude"]]
[["continent : Americas","city : Atlanta II",1,"range"],"136.226.2.0/23"]
[["continent : Americas","city : Atlanta II",1,"vpn"],""]
[["continent : Americas","city : Atlanta II",1,"gre"],"104.129.204.32"]
[["continent : Americas","city : Atlanta II",1,"hostname"],""]
[["continent : Americas","city : Atlanta II",1,"latitude"],"34"]
[["continent : Americas","city : Atlanta II",1,"longitude"],"-84"]
[["continent : Americas","city : Atlanta II",1,"longitude"]]
[["continent : Americas","city : Atlanta II",1]]
[["continent : Americas","city : Atlanta II"]]
[["continent : Americas"]]
Вы можете видеть в приведенном выше результате, что каждая строка, где путь к элементу потока (первый элемент массива, который является массивом), который заканчивается на "range"
, имеет все поля в желаемом выводе. .[0][-1]
обращается к последнему элементу массива первого массива для каждого элемента потока результатов выше. Остальное — просто фильтрация и форматирование.
Ограничьте данные конвейера только строками «диапазона»:
.["zscaler.net"] | tostream
| select(.[0][-1] == "range")
Результат:
[["continent : EMEA","city : Amsterdam II",0,"range"],"165.225.240.0/23"]
[["continent : EMEA","city : Amsterdam II",1,"range"],"147.161.132.0/23"]
[["continent : EMEA","city : Brussels II",0,"range"],"147.161.156.0/23"]
[["continent : Americas","city : Atlanta II",0,"range"],"104.129.204.0/23"]
[["continent : Americas","city : Atlanta II",1,"range"],"136.226.2.0/23"]
Чтобы использовать join(",")
в конце для создания нужного CSV, данные необходимо «извлечь» и поместить в массив.
Из приведенного выше результата доступ к значению "range"
осуществляется с помощью .[-1]
, то есть последнего элемента массива. "continent"
и "city"
— это первый и второй элементы массива, который сам является первым элементом всего массива, т. е. .[0][0:2]
. Просто нужно извлечь значение из этих элементов, и это можно сделать с помощью split(":")
. Поскольку это необходимо сделать для каждого элемента, вы можете повторить каждый элемент с помощью .[0][0:2][]
. Передача этого в split(":")
форматирует вывод по желанию.
.["zscaler.net"] | tostream
| select(.[0][-1] == "range")
| [.[-1], .[0][0:2][]|split(":")[-1]]
Результат:
["165.225.240.0/23"," EMEA"," Amsterdam II"]
["147.161.132.0/23"," EMEA"," Amsterdam II"]
["147.161.156.0/23"," EMEA"," Brussels II"]
["104.129.204.0/23"," Americas"," Atlanta II"]
["136.226.2.0/23"," Americas"," Atlanta II"]
Осталось только отформатировать вывод в формате CSV с помощью join(",")
, как показано в исходном решении выше.
Вау! Это совсем сокращение. Но это работает как шарм. Не могли бы вы объяснить, что вы сделали?
@deadshotpro Я расширил свой ответ дополнительными пояснениями.
Вам это нужно для
"continent : EMEA"
или для всех городов?