web-dev-qa-db-ja.com

JSONオブジェクトをBash連想配列に変換する

Bashスクリプトがあります。 JSONでデータを取得します。 JSON配列をBash配列に変換する必要があります。

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

バッシュでは、このような値を取得したいですecho ${arr[SOMETHING]}

18
Evgenii

キーと値が必要な場合、そして jsonオブジェクトをJQのkey = value形式に変換するにはどうすればよいですか に基づいて、次のことができます:

_$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
_

より一般的な方法では、jqwhilewhile ... do; ... done < <(command)構文で提供するだけで、値を配列_myarray[key] = value_に次のように格納できます。

_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
_

コンテキスト:この回答は、存在しない質問のタイトルに対応するように作成されました。


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")')
8
Charles Duffy

この質問に対する回答はありますが、投稿された回答から要件を完全に満たすことはできませんでした。以下は、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
}
_

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/--null-inputオプション+ --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
}
_

以前の回答を確認する

@JánLalinský

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のkey = value形式に変換する方法 に基づいて、次のことができます:

_$ jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" file
SALUTATION=Hello world
SOMETHING=bla bla bla Mr. Freeman
_

より一般的な方法では、jqwhile ... do; ... done < <(command)構文を使用してwhileを提供するだけで、値を配列_myarray[key] = value_に格納できます。

_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の配列をループ処理することです。

各ソリューションは有効であり、ユースケースによっては、一方が他方よりも役立つ場合があります。

6
Sid

これは、再帰的に実行できる方法です。

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

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が実行する可能性のある悪意のあるシェルコマンドが含まれている可能性があります)。

0
Ján Lalinský