将JSON对象转换为Bash关联数组

时间:2014-11-03 15:21:13

标签: arrays json bash jq

我有一个Bash脚本。它以JSON获取数据。 我需要将JSON数组转换为Bash数组。

实施例

{
  "SALUTATION": "Hello world",
  "SOMETHING": "bla bla bla Mr. Freeman"
}

在Bash中,我希望得到一个像echo ${arr[SOMETHING]}这样的值。

7 个答案:

答案 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周围使用引号(keysvalues)。   #!/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
}

将JSON插入JQ

#!/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
}

jq -n /-空输入选项+ --argfile &&重定向

# 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"