У меня есть объект в JSON со структурой, показанной ниже, и я хочу преобразовать его в ассоциативный массив BASH "поддельный 3D" со следующей структурой:
SHIPS[$mmsi:mmsi]=367513050
SHIPS[$mmsi:lat]=42.380329
...etc
Другими словами, я хочу, чтобы значение mmsi
в сочетании с ключом каждого объекта стало индексом ассоциативного массива.
Прямо сейчас я делаю следующее. Недостатком является то, что он полагается на то, что поле mmsi
будет первым полем каждого объекта JSON, что, вероятно, верно, но также не очень надежно.
Это предоставляет 3 объекта: VESSEL_INDEX
со всеми mmsi
s, KEY_INDEX
со всеми существующими ключами и VESSELS
, к которым можно получить доступ как VESSELS[$mmsi:$key]
Обновлено: для ясности/полноты моей конечной целью является:
curl
в файле JSON из URL-адресаVESSELS[$mmsi:$key]
) с ключами/значениями, предоставленными в объекте JSON, оставив без изменений любые неупомянутые элементы массива. declare -A VESSELS
declare -a VESSEL_INDEX
declare -a KEY_INDEX
while read -r keyvalue
do
key = "${keyvalue%%=*}"
value = "${keyvalue#*=}"
if [[ "$key" == "mmsi" ]]
then
mmsi = "$value"
[[ -n "$mmsi" ]] && VESSEL_INDEX+=("$mmsi")
fi
[[ ! " ${KEY_INDEX[*]} " =~ " ${key} " ]] && KEY_INDEX+=("${key}")
[[ -n "$mmsi" ]] && VESSELS["$mmsi:$key"] = "$value"
done <<< "$(curl -sL "$AIS_URL/ships_full.json" | jq -r ".ships[]|to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")"
с этим вводом, свернутым:
{
"count": 2
"ships": [
{
"mmsi": 367513050,
"lat": 42.380329,
"lon": -71.042946,
"distance": 0.317654,
"mmsi_type": 1,
"level": -28.161266,
"count": 6895,
"ppm": 4.918982,
"heading": null,
"cog": null,
"speed": 0,
"to_bow": 10,
"to_stern": 17,
"to_starboard": 3,
"to_port": 5,
"shiptype": 52,
"msg_type": 0,
"country": "US",
"status": 0,
"callsign": "WDG2188",
"shipname": "VINCENT D. TIBBETTS",
"destination": "BOSTON",
"last_signal": 0
},
{
"mmsi": 367447520,
"lat": 42.324032,
"lon": -70.994347,
"distance": 3.777312,
"mmsi_type": 1,
"level": -37.657475,
"count": 1103,
"ppm": -1.157407,
"heading": 156,
"cog": 155.5,
"speed": 28.4,
"to_bow": 24,
"to_stern": 20,
"to_starboard": 9,
"to_port": 4,
"shiptype": 40,
"msg_type": 0,
"country": "US",
"status": 0,
"callsign": "WDF4062",
"shipname": "SALACIA",
"destination": "XX XXX>?? ???",
"last_signal": 0
},
],
"error": false
}
Заставьте jq
создать оператор declare
, используя скобки вокруг ключа и экранируя с помощью @sh
:
unset SHIPS
declare -A SHIPS = "($(jq -r '
.ships[] | .mmsi as $mmsi
| to_entries[] | @sh "[\("\($mmsi):\(.key)")]=\(.value)"
' ships_full.json ))"
Теперь вы сможете использовать ассоциативный массив $SHIPS
в Bash:
echo "${SHIPS["367513050:lat"]}"
42.380329
@ kx1t Конечно, это не удается, потому что SHIPS = "(['key']='value')"
это не то, как вы объявляете массивы или присваиваете значения элементам в Bash. Пожалуйста, укажите, чего вы хотите достичь. Если вам нужно одно назначение для каждой пары ключ-значение, то есть SHIPS['key']='value'
, просто соответствующим образом измените выходные данные фильтра jq. Однако имейте в виду, что такое присваивание больше не является оператором declare
, поэтому вам нужно будет eval
вывести результат, чтобы он был интерпретирован Bash.
В конце концов, я хочу свернуть JSON (например, с помощью < <(curl -sSL https://kx1t.com/ais/ships.json)
) и обновить с его помощью существующий массив asoc SHIPS
. Любые возвращенные записи будут обновлены из JSON в ${SHIPS[$mmsi:$key]}
, а любые другие записи в массиве останутся на месте. В результате я хочу отдельно объявить связанный массив, а потом сделать SHIPS+=( ... )
@ kx1t Что касается ввода, просто укажите его на стандартном вводе так, как вы предпочитаете, например. curl … | jq …
или jq … <(curl …)
или тому подобное. Что касается обновления массива, вы в основном хотите прочитать (новый) массив, как описано выше, а затем объединить его с уже существующим.
Основываясь на вашем втором комментарии, у меня есть это, и, похоже, оно работает: unset VESSELS; declare -A VESSELS; eval "$(jq -r '.ships[] | .mmsi as $mmsi | to_entries[] | @sh "VESSELS[\("\($mmsi):\(.key)")]=\(.value)"' < <(curl -sSL http://kx1t.com/ais/ships.json) )"; declare -p VESSELS
Я добавлю его как отдельный ответ на свой вопрос и припишу вам за него. Еще раз спасибо!
Благодаря ответам и подсказкам @pmf теперь у меня есть следующее, которое работает. Выкладываю сюда, чтобы другие могли увидеть:
unset VESSELS
declare -A VESSELS
# ... other code here
eval "$(jq -r '.ships[] | .mmsi as $mmsi | to_entries[] | @sh "VESSELS[\("\($mmsi):\(.key)")]=\(.value)"' < <(curl -sSL http://xxxx.com/ais/ships.json) )"
# check that it worked:
declare -p VESSELS
Tnx. Итак, я столкнулся с проблемой с вашим ответом: он отлично работает, если используется точно так, как вы написали, но он объединяет все выходные данные в один аргумент, если вы разделяете операторы
declare
и присваивания. (unset SHIPS; declare -A SHIPS; SHIPS = "($(jq -r ...
)