Преобразование объекта JSON в ассоциативный массив Bash
У меня есть Bash script. Он получает данные в JSON.
Мне нужно преобразовать массив JSON в массив Bash.
Пример
{
"SALUTATION": "Hello world",
"SOMETHING": "bla bla bla Mr. Freeman"
}
В Bash я хочу получить значение, подобное этому echo ${arr[SOMETHING]}
.
Ответы
Ответ 1
Если вам нужны ключ и значение, и на основе Как преобразовать объект json в формат ключ = значение в JQ, вы можете сделать следующее:
$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
В более общем смысле вы можете сохранить значения в массиве myarray[key] = value
следующим образом, просто предоставив jq
для while
с синтаксисом while ... do; ... done < <(command)
:
declare -A myarray
while IFS="=" read -r key value
do
myarray[$key]="$value"
done < <(jq -r 'to_entries|map("(.key)=(.value)")|.[]' file)
А затем вы можете просмотреть значения в следующем виде:
for key in "${!myarray[@]}"
do
echo "$key = ${myarray[$key]}"
done
Для данного ввода он возвращает:
SALUTATION = Hello world
SOMETHING = bla bla bla Mr. Freeman
Ответ 2
Контекст. Этот ответ был написан с учетом названия вопроса, которого больше не существует..
ОП вопрос фактически описывает объекты, а не массивы.
Чтобы быть уверенным, что мы помогаем другим людям, которые на самом деле ищут помощь с массивами JSON, стоит их подробно рассказать.
Для случая безопасного перехода, когда строки не могут содержать переводы строки (и когда используется bash 4.0 или более поздняя версия), это работает:
str='["Hello world", "bla bla bla Mr. Freeman"]'
readarray -t array <<<"$(jq -r '.[]' <<<"$str")"
Чтобы поддерживать более старые версии bash и строки с символами новой строки, мы немного потренировались, используя поток с разделителями NUL для чтения из jq
:
str='["Hello world", "bla bla bla Mr. Freeman", "this is\ntwo lines"]'
array=( )
while IFS= read -r -d '' line; do
array+=( "$line" )
done < <(jq -j '.[] | (. + "\u0000")')
Ответ 3
Хотя на этот вопрос дан ответ, я не смог полностью насытить
требования из размещенного ответа. Вот немного напиши что поможет любому
Баш -n ewcomers.
Предвидение
Объявление базового ассоциативного массива
#!/bin/bash
declare -A associativeArray=([key1]=val1 [key2]=val2)
Вы также можете использовать кавычки ('
, "
) вокруг declaration
, его keys
и values
.
#!/bin/bash
declare -A 'associativeArray=([key1]=val1 [key2]=val2)'
И вы можете разделить каждую пару [key]=value
пробелом или новой строкой.
#!/bin/bash
declare -A associativeArray([key1]=value1
['key2']=value2 [key3]='value3'
['key4']='value2' ["key5"]="value3"
["key6"]='value4'
['key7']="value5"
)
В зависимости от варианта котировки может потребоваться экранировать строку.
Использование Indirection для доступа к ключу и значению в ассоциативном массиве
function example {
local -A associativeArray=([key1]=val1 [key2]=val2)
# print associative array
local key value
for key in "${!associativeArray[@]}"; do
value="${associativeArray["$key"]}"
printf '%s = %s' "$key" "$value"
done
}
Запуск примера функции
$ example
key2 = val2
key1 = val1
Зная вышеупомянутые лакомые кусочки, вы можете получить следующие фрагменты:
Следующие примеры будут иметь результат, как в примере выше
Оценка строки
#!/usr/bin/env bash
function example {
local arrayAsString='associativeArray=([key1]=val1 [key2]=val2)'
local -A "$arrayAsString"
# print associative array
}
Трубопровод JSON в JQ
#!/usr/bin/env bash
function example {
# Given the following JSON
local json='{ "key1": "val1", "key2": "val2" }'
# filter using 'map' && 'reduce'
local filter='to_entries | map("[\(.key)]=\(.value)") |
reduce .[] as $item ("associativeArray=("; . + ($item|@sh) + " ") + ")"'
# Declare and assign separately to avoid masking return values.
local arrayAsString;
arrayAsString=$(cat "$json" | jq --raw-output "${filter}")
local -A "$arrayAsString"
# print associative array
}
jq -n / - -n опция универсального ввода + --argfile & перенаправление
#!/usr/bin/env bash
function example {
# /path/to/file.json contains the same json as the first two examples
local filter filename='/path/to/file.json'
# including bash variable name in reduction
filter='to_entries | map("[\(.key | @sh)]=\(.value | @sh) ")
| "associativeArray=(" + add + ")"'
# using --argfile && --null-input
local -A "$(jq --raw-output --null-input --argfile file "$filename" \
"\$filename | ${filter}")"
# or for a more traceable declaration (using shellcheck or other) this
# variation moves the variable name outside of the string
# map definition && reduce replacement
filter='[to_entries[]|"["+(.key|@sh)+"]="+(.value|@sh)]|"("+join(" ")+")"'
# input redirection && --join-output
local -A associativeArray=$(jq --join-output "${filter}" < "${filename}")
# print associative array
}
Просмотр предыдущих ответов
@Ян Лалинский
Для эффективной загрузки объекта JSON в ассоциативный массив bash (без использования циклов в bash) можно использовать инструмент "jq" следующим образом.
# first, load the json text into a variable:
json='{"SALUTATION": "Hello world", "SOMETHING": "bla bla bla Mr. Freeman"}'
# then, prepare associative array, I use 'aa':
unset aa
declare -A aa
# use jq to produce text defining name:value pairs in the bash format
# using @sh to properly escape the values
aacontent=$(jq -r '. | to_entries | .[] | "[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")
# string containing whole definition of aa in bash
aadef="aa=($aacontent)"
# load the definition (because values may contain LF characters, aadef must be in double quotes)
eval "$aadef"
# now we can access the values like this: echo "${aa[SOMETHING]}"
Предупреждение: здесь используется eval, что опасно, если ввод json происходит из неизвестного источника (может содержать вредоносные команды оболочки, которые может выполнять eval).
Это может быть сокращено до следующего
function example {
local json='{ "key1": "val1", "key2": "val2" }'
local -A associativeArray=("$(jq -r '. | to_entries | .[] |
"[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")")
# print associative array
}
@fedorqui
Если вам нужны ключ и значение, и на основе Как преобразовать объект json в формат ключ = значение в JQ, вы можете сделать следующее:
$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
В более общем смысле вы можете сохранить значения в массиве myarray[key] = value
следующим образом, просто предоставив jq
для while
с синтаксисом while ... do; ... done < <(command)
:
declare -A myarray
while IFS="=" read -r key value
do
myarray[$key]="$value"
done < <(jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]" file)
А затем вы можете просмотреть значения в следующем виде:
for key in "${!myarray[@]}"
do
echo "$key = ${myarray[$key]}"
done
Для данного ввода он возвращает:
SALUTATION = Hello world
SOMETHING = bla bla bla Mr. Freeman
Основное различие между этим и моим решением заключается в
массив в bash или в jq.
Каждое решение является действительным и, в зависимости от вашего варианта использования, может быть более полезным
тогда другой.
Ответ 4
Вот как это можно сделать рекурсивно:
#!/bin/bash
SOURCE="$PWD"
SETTINGS_FILE="$SOURCE/settings.json"
SETTINGS_JSON=`cat "$SETTINGS_FILE"`
declare -A SETTINGS
function get_settings() {
local PARAMS="$#"
local JSON=`jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" <<< "$1"`
local KEYS=''
if [ $# -gt 1 ]; then
KEYS="$2"
fi
while read -r PAIR; do
local KEY=''
if [ -z "$PAIR" ]; then
break
fi
IFS== read PAIR_KEY PAIR_VALUE <<< "$PAIR"
if [ -z "$KEYS" ]; then
KEY="$PAIR_KEY"
else
KEY="$KEYS:$PAIR_KEY"
fi
if jq -e . >/dev/null 2>&1 <<< "$PAIR_VALUE"; then
get_settings "$PAIR_VALUE" "$KEY"
else
SETTINGS["$KEY"]="$PAIR_VALUE"
fi
done <<< "$JSON"
}
Чтобы вызвать его:
get_settings "$SETTINGS_JSON"
Доступ к массиву будет таким:
${SETTINGS[grandparent:parent:child]}
Ответ 5
Для эффективной загрузки объекта JSON в ассоциативный массив bash (без использования циклов в bash) можно использовать инструмент "jq" следующим образом.
# first, load the json text into a variable:
json='{"SALUTATION": "Hello world", "SOMETHING": "bla bla bla Mr. Freeman"}'
# then, prepare associative array, I use 'aa':
unset aa
declare -A aa
# use jq to produce text defining name:value pairs in the bash format
# using @sh to properly escape the values
aacontent=$(jq -r '. | to_entries | .[] | "[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")
# string containing whole definition of aa in bash
aadef="aa=($aacontent)"
# load the definition (because values may contain LF characters, aadef must be in double quotes)
eval "$aadef"
# now we can access the values like this: echo "${aa[SOMETHING]}"
Предупреждение: здесь используется eval, что опасно, если ввод json происходит из неизвестного источника (может содержать вредоносные команды оболочки, которые может выполнять eval).