我有一个Bash脚本。它以JSON获取数据。 我需要将JSON数组转换为Bash数组。
实施例
{
"SALUTATION": "Hello world",
"SOMETHING": "bla bla bla Mr. Freeman"
}
在Bash中,我希望得到一个像echo ${arr[SOMETHING]}
这样的值。
答案 0 :(得分:18)
如果您需要密钥和值,并且基于How do i convert a json object to key=value format in 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)
:< / p>
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
答案 1 :(得分:7)
上下文:这个答案是为了回应不再存在的问题标题。 。
OP的问题实际上描述了对象和数组。
为了确保我们帮助那些实际寻找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")')
答案 2 :(得分:3)
尽管这个问题得到了回答,但我还是不能完全满足我的要求。 发布的答案中的要求。这里有一点点写对您有帮助 bash新手。
基本的关联数组声明
#!/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"
)
。
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
}
了解了上述花絮,您就可以得出以下摘要:
以下示例将具有与上面示例相同的结果
#!/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
}
#!/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
}
# 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]}"
@JánLalinský
有效地将JSON对象加载到bash关联数组中 (不使用bash中的循环),可以使用工具“ jq”,如下所示。
function example { local json='{ "key1": "val1", "key2": "val2" }' local -A associativeArray=("$(jq -r '. | to_entries | .[] | "[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")") # print associative array }
警告:使用eval,如果json输入来自未知来源(可能包含eval可能执行的恶意shell命令),这将很危险。
这可以简化为以下内容
$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
@fedorqui
如果您想要键和值,并且基于How do i convert a json object to key=value format in JQ,则可以执行以下操作:
myarray[key] = value
以更一般的方式,您可以将值
jq
提供给while
,语法为while ... do; ... done < <(command)
: / p>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中的数组。
每种解决方案均有效,并且取决于您的用例,可能更有用 然后是另一个。
答案 3 :(得分:1)
这是如何以递归方式完成的:
#!/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]}
答案 4 :(得分:1)
基于@HelpNeeder 的 solution(顺便说一句不错)
他的解决方案并不真正适用于整数,所以我做了一些补充。扩展的条件检查量,因此可以说牺牲了一些性能。
此版本适用于整数和浮点值。
SOURCE="$PWD"
SETTINGS_FILE="./test2.json"
SETTINGS_JSON=`cat "$SETTINGS_FILE"`
declare -A SETTINGS
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
res=$(jq -e . 2>/dev/null <<< "$PAIR_VALUE")
exitCode=$?
check=`echo "$PAIR_VALUE" | grep -E ^\-?[0-9]*\.?[0-9]+$`
# if [ "${res}" ] && [ $exitCode -eq "0" ] && [[ ! "${PAIR_VALUE}" == ?(-)+([0-9]) ]] ALTERNATIVE, works only for integer (not floating point)
if [ "${res}" ] && [ $exitCode -eq "0" ] && [[ "$check" == '' ]]
then
get_settings "$PAIR_VALUE" "$KEY"
else
SETTINGS["$KEY"]="$PAIR_VALUE"
fi
done <<< "$JSON"
}
get_settings "$SETTINGS_JSON"
答案 5 :(得分:0)
要有效地将JSON对象加载到bash关联数组中(不使用bash中的循环),可以使用工具&#39; jq&#39;,如下所示。
# 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命令),这是危险的。
答案 6 :(得分:0)
解决方案:使用jq
(这是一种轻巧灵活的命令行JSON处理器。)
在bash中,我宁愿将JSONs对象分配给一个变量,并使用jq
来访问和解析正确的结果。它比用数组解析此结构更方便,并且它具有多种功能和特性,例如访问嵌套和复杂的对象,选择方法,内置运算符和函数,正则表达式支持,比较等。
示例:
example='{"SALUTATION": "Hello world","SOMETHING": "bla bla bla Mr. Freeman"}'
echo $example | jq .SOMETHING
# output:
"bla bla bla Mr. Freeman"