将 JSON 对象转换为 Bash 关联数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26717277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Converting a JSON object into a Bash associative array
提问by Evgenii
I have a Bash script. It gets data in JSON. I need to convert the JSON array to a Bash array.
我有一个 Bash 脚本。它以 JSON 格式获取数据。我需要将 JSON 数组转换为 Bash 数组。
Example
例子
{
"SALUTATION": "Hello world",
"SOMETHING": "bla bla bla Mr. Freeman"
}
In Bash I want to get a value like this echo ${arr[SOMETHING]}.
在 Bash 中,我想获得这样的值echo ${arr[SOMETHING]}。
回答by fedorqui 'SO stop harming'
If you want key and value, and based on How do i convert a json object to key=value format in JQ, you can do:
如果您想要键和值,并且基于如何将 json 对象转换为 JQ 中的键=值格式,您可以执行以下操作:
$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
In a more general way, you can store the values into an array myarray[key] = valuelike this, just by providing jqto the whilewith the while ... do; ... done < <(command)syntax:
在更普遍的方式,你可以将值存储到一个数组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)
And then you can loop through the values like this:
然后你可以循环遍历这样的值:
for key in "${!myarray[@]}"
do
echo "$key = ${myarray[$key]}"
done
For this given input, it returns:
对于这个给定的输入,它返回:
SALUTATION = Hello world
SOMETHING = bla bla bla Mr. Freeman
回答by Charles Duffy
Context: This answer was written to be responsive to a question title which no longer exists..
上下文:编写此答案是为了响应不再存在的问题标题。.
The OP's question actually describes objects, vs arrays.
OP 的问题实际上描述了对象与数组。
To be sure that we help other people coming in who are actuallylooking for help with JSON arrays, though, it's worth covering them explicitly.
不过,为了确保我们帮助其他真正寻求 JSON 数组帮助的人,值得明确地介绍他们。
For the safe-ish case where strings can't contain newlines (and when bash 4.0 or newer is in use), this works:
对于字符串不能包含换行符的安全情况(以及使用 bash 4.0 或更新版本时),这有效:
str='["Hello world", "bla bla bla Mr. Freeman"]'
readarray -t array <<<"$(jq -r '.[]' <<<"$str")"
To support older versions of bash, and strings with newlines, we get a bit fancier, using a NUL-delimited stream to read from jq:
为了支持旧版本的 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")')
回答by Sid
Although this question is answered, I wasn't able to fully satiate my requirements from the posted answer. Here is a little write up that'll help any bash-newcomers.
虽然这个问题得到了回答,但我无法从发布的答案中完全满足我的要求。这里有一篇小文章,可以帮助任何 bash 新手。
Foreknowledge
预知
A basic associative array declaration
一个基本的关联数组声明
#!/bin/bash
declare -A associativeArray=([key1]=val1 [key2]=val2)
You can also use quotes (', ") around the declaration, its keys, and
values.
您还可以在、 its和
周围使用引号 ( ', ") 。declarationkeysvalues
#!/bin/bash
declare -A 'associativeArray=([key1]=val1 [key2]=val2)'
And you can delimit each [key]=valuepair via space or newline.
您可以[key]=value通过空格或换行符分隔每一对。
#!/bin/bash
declare -A associativeArray([key1]=value1
['key2']=value2 [key3]='value3'
['key4']='value2' ["key5"]="value3"
["key6"]='value4'
['key7']="value5"
)
Depending on your quote variation, you may need to escape your string.
根据您的报价变体,您可能需要对字符串进行转义。
Using Indirection to access both key and value in an associative array
使用间接访问关联数组中的键和值
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
}
Running the example function
运行示例函数
$ example
key2 = val2
key1 = val1
Knowing the aforementioned tidbits allows you to derive the following snippets:
了解上述花絮可以让您得出以下片段:
The following examples will all have the result as the example above
下面的例子都会有上面例子的结果
String evaluation
字符串评估
#!/usr/bin/env bash
example () {
local arrayAsString='associativeArray=([key1]=val1 [key2]=val2)'
local -A "$arrayAsString"
# print associative array
}
Piping your JSON into JQ
将您的 JSON 传输到 JQ
#!/usr/bin/env bash
# Note: usage of single quotes instead of double quotes for the jq
# filter. The former is preferred to avoid issues with shell
# substitution of quoted strings.
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;
# Note: no encompassing quotation (")
arrayAsString=$(cat "$json" | jq --raw-output "${filter}")
local -A "$arrayAsString"
# print associative array
}
jq -n / --null-input option + --argfile && redirection
jq -n / --null-input 选项 + --argfile && 重定向
#!/usr/bin/env bash
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
}
Reviewing previous answers
回顾以前的答案
@Ján Lalinsky
@扬·拉林斯基
To load JSON object into a bash associative array efficiently (without using loops in bash), one can use tool 'jq', as follows.
# 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]}"Warning:this uses eval, which is dangerous if the json input is from unknown source (may contain malicious shell commands that eval may execute).
要有效地将 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 可能执行的恶意 shell 命令),这很危险。
This could be reduced to the following
这可以简化为以下
example () {
local json='{ "key1": "val1", "key2": "val2" }'
local -A associativeArray=("$(jq -r '. | to_entries | .[] |
"[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")")
# print associative array
}
@fedorqui
@fedorqui
If you want key and value, and based on How do i convert a json object to key=value format in JQ, you can do:
$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file SALUTATION=Hello world SOMETHING=bla bla bla Mr. FreemanIn a more general way, you can store the values into an array
myarray[key] = valuelike this, just by providingjqto thewhilewith thewhile ... do; ... done < <(command)syntax:declare -A myarray while IFS="=" read -r key value do myarray[$key]="$value" done < <(jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]" file)And then you can loop through the values like this:
for key in "${!myarray[@]}" do echo "$key = ${myarray[$key]}" doneFor this given input, it returns:
SALUTATION = Hello world SOMETHING = bla bla bla Mr. Freeman
如果您想要键和值,并且基于如何将 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
The main difference between this solution and my own is looping through the array in bash or in jq.
这个解决方案和我自己的解决方案之间的主要区别是在 bash 或 jq 中循环遍历数组。
Each solution is valid and depending on your use case, one may be more useful then the other.
每个解决方案都是有效的,并且根据您的用例,一个可能比另一个更有用。
回答by HelpNeeder
This is how can it be done recursively:
这是如何递归完成的:
#!/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)\")|.[]" <<< ""`
local KEYS=''
if [ $# -gt 1 ]; then
KEYS=""
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"
}
To call it:
调用它:
get_settings "$SETTINGS_JSON"
The array will be accessed like so:
该数组将像这样访问:
${SETTINGS[grandparent:parent:child]}
回答by Ján Lalinsky
To load JSON object into a bash associative array efficiently (without using loops in bash), one can use tool 'jq', as follows.
要有效地将 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]}"
Warning:this uses eval, which is dangerous if the json input is from unknown source (may contain malicious shell commands that eval may execute).
警告:这使用了 eval,如果 json 输入来自未知来源(可能包含 eval 可能执行的恶意 shell 命令),这很危险。

