web-dev-qa-db-fra.com

Conversion d'un objet JSON en un tableau associatif Bash

J'ai un script Bash. Il obtient les données au format JSON. Je dois convertir le tableau JSON en un tableau Bash.

Exemple

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

Dans Bash, je veux obtenir une valeur comme celle-ci echo ${arr[SOMETHING]}.

13
Evgenii

Si vous voulez une clé et une valeur, et basé sur Comment convertir un objet Json au format clé = valeur dans JQ , vous pouvez faire:

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

De manière plus générale, vous pouvez stocker les valeurs dans un tableau myarray[key] = value comme ceci, simplement en fournissant jq à while avec la syntaxe 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)

Et ensuite, vous pouvez parcourir les valeurs suivantes:

for key in "${!myarray[@]}"
do
    echo "$key = ${myarray[$key]}"
done

Pour cette entrée donnée, il retourne:

SALUTATION = Hello world
SOMETHING = bla bla bla Mr. Freeman
17
fedorqui

 Contexte: Cette réponse a été écrite pour répondre à un titre de question qui n'existe plus. .


La question du PO décrit en fait les objets, vs les tableaux.

Pour être sûr que nous aidons les autres personnes qui viennent en fait recherchons de l'aide avec les tableaux JSON, il vaut toutefois la peine de les couvrir explicitement.


Dans le cas où les chaînes ne peuvent pas contenir de nouvelles lignes (et lorsque bash 4.0 ou plus récent est utilisé), cela fonctionne:

str='["Hello world", "bla bla bla Mr. Freeman"]'
readarray -t array <<<"$(jq -r '.[]' <<<"$str")"

Pour prendre en charge les anciennes versions de bash et des chaînes avec des nouvelles lignes, nous obtenons un peu plus sophistiqué, en utilisant un flux délimité par NUL pour lire à partir de 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")')
7
Charles Duffy

Bien que cette question reçoive une réponse, je n’ai pas été en mesure de satisfaire pleinement mes exigences de Par rapport à la réponse affichée. Voici un petit article qui aidera tous les nouveaux arrivants .

Connaissance anticipée

Une déclaration de tableau associatif de base

#!/bin/bash

declare -A associativeArray=([key1]=val1 [key2]=val2)

Vous pouvez également utiliser des guillemets (', ") autour de declaration, ses keys et values.

#!/bin/bash

declare -A 'associativeArray=([key1]=val1 [key2]=val2)'

Et vous pouvez délimiter chaque paire [key]=value via espace ou nouvelle ligne.

#!/bin/bash

declare -A associativeArray([key1]=value1
  ['key2']=value2 [key3]='value3'
  ['key4']='value2'               ["key5"]="value3"


  ["key6"]='value4'
  ['key7']="value5"
)

En fonction de la variation de votre devis, vous devrez peut-être échapper votre chaîne.

Utilisation de Indirection pour accéder à la fois à la clé et à la valeur dans un tableau associatif

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
}

Exécuter la fonction exemple

$ example
key2 = val2
key1 = val1

Connaître les friandises susmentionnées vous permet de dériver les extraits suivants:


Les exemples suivants auront tous le résultat comme l'exemple ci-dessus

Évaluation de la chaîne

#!/usr/bin/env bash

function example {
  local arrayAsString='associativeArray=([key1]=val1 [key2]=val2)'
  local -A "$arrayAsString"

  # print associative array
}

Piping de votre JSON dans 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
}

option jq -n/--null-input + --argfile && redirection

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

Revoir les réponses précédentes

@ Ján Lalinský

Pour charger efficacement un objet JSON dans un tableau associatif bash (Sans utiliser de boucles dans bash), vous pouvez utiliser l'outil 'jq', comme suit.

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

Attention: ceci utilise eval, ce qui est dangereux si l'entrée json provient d'une source inconnue (peut contenir des commandes Shell malveillantes pouvant être exécutées par eval).

Cela pourrait être réduit à la suivante

function example {
  local json='{ "key1": "val1", "key2": "val2" }'
  local -A associativeArray=("$(jq -r '. | to_entries | .[] |
    "[\"" + .key + "\"]=" + (.value | @sh)' <<< "$json")")

  # print associative array
}

@fedorqui

Si vous voulez une clé et une valeur, et basé sur Comment puis-je convertir un objet Json au format clé = valeur dans JQ , vous pouvez faire:

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

De manière plus générale, vous pouvez stocker les valeurs dans un tableau myarray[key] = value comme ceci, simplement en fournissant jq à la while avec la syntaxe 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)

Et ensuite, vous pouvez parcourir les valeurs suivantes:

for key in "${!myarray[@]}"
do
    echo "$key = ${myarray[$key]}"
done

Pour cette entrée donnée, il retourne:

SALUTATION = Hello world
SOMETHING = bla bla bla Mr. Freeman

La principale différence entre cette solution et la mienne réside dans le tableau En bash ou en jq.

Chaque solution est valide et, selon votre cas d'utilisation, l'une peut être plus utile Que l'autre.

3
Sid

Voici comment cela peut être fait de manière récursive:

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

Pour l'appeler:

get_settings "$SETTINGS_JSON"

Le tableau sera accédé comme ceci:

${SETTINGS[grandparent:parent:child]}
1
HelpNeeder

Pour charger efficacement un objet JSON dans un tableau associatif bash (sans utiliser de boucles dans bash), vous pouvez utiliser l'outil 'jq', comme suit. 

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

Attention: ceci utilise eval, ce qui est dangereux si l'entrée json provient d'une source inconnue (peut contenir des commandes Shell malveillantes pouvant être exécutées par eval).

0
Ján Lalinský