it-swarm.com.de

Konvertieren eines JSON-Objekts in ein assoziatives Bash-Array

Ich habe ein Bash-Skript. Es erhält Daten in JSON. Ich muss das JSON-Array in ein Bash-Array konvertieren.

Beispiel

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

In Bash möchte ich einen Wert wie diesen echo ${arr[SOMETHING]} bekommen.

13
Evgenii

Wenn Sie Schlüssel und Wert wünschen und basierend auf Wie konvertiere ich ein Json-Objekt in JQ in das Format key = value, können Sie Folgendes tun:

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

Ganz allgemein können Sie die Werte in einem Array myarray[key] = value wie folgt speichern, indem Sie einfach jq der while mit der while ... do; ... done < <(command)-Syntax zur Verfügung stellen:

declare -A myarray
while IFS="=" read -r key value
do
    myarray[$key]="$value"
done < <(jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]" file)

Und dann können Sie die Werte wie folgt durchlaufen:

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

Für diese Eingabe wird Folgendes zurückgegeben:

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

 Kontext: Diese Antwort wurde geschrieben, um auf einen Fragetitel zu antworten, der nicht mehr existiert. .


Die Frage des OP beschreibt tatsächlich Objekte und Arrays.

Um sicher zu sein, dass wir anderen Leuten helfen, die tatsächlich auf der Suche nach Hilfe bei JSON-Arrays sind, lohnt es sich jedoch, sie explizit zu behandeln.


Für den sicheren Fall, in dem Zeichenfolgen keine Zeilenumbrüche enthalten können (und wenn bash 4.0 oder neuer verwendet wird), funktioniert dies:

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

Um ältere Versionen von bash und Strings mit Zeilenumbrüchen zu unterstützen, werden wir etwas schicker, indem wir einen NUL-getrennten Stream zum Lesen von jq verwenden:

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

Obwohl diese Frage beantwortet wurde, konnte ich meine -Anforderungen nicht vollständig mit der veröffentlichten Antwort zufriedenstellen. Hier ist ein kleiner Bericht, der jedem Bash-Newcomer helfen wird.

Vorwissen

Eine grundlegende assoziative Arraydeklaration

#!/bin/bash

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

Sie können auch Anführungszeichen (', ") für die declaration, deren keys und values verwenden.

#!/bin/bash

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

Sie können jedes [key]=value-Paar durch Leerzeichen oder Zeilenumbrüche begrenzen.

#!/bin/bash

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


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

Abhängig von Ihrer Angebotsvariante müssen Sie die Zeichenfolge möglicherweise mit einem Escapezeichen versehen.

Indirection verwenden, um auf Schlüssel und Wert in einem assoziativen Array zuzugreifen

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
}

Ausführen der Beispielfunktion

$ example
key2 = val2
key1 = val1

Wenn Sie die oben genannten Leckerbissen kennen, können Sie die folgenden Ausschnitte ableiten:


Die folgenden Beispiele haben alle das Ergebnis wie im obigen Beispiel

String-Auswertung

#!/usr/bin/env bash

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

  # print associative array
}

Pipe deine JSON in 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-option + --argfile && umleitung

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

Frühere Antworten überprüfen

@ Ján Lalinský

Um das JSON-Objekt effizient in ein assoziatives Bash-Array Zu laden (ohne Schleifen in der Bash zu verwenden), kann das Tool 'jq' wie folgt verwendet werden.

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

Warnung: verwendet eval, was gefährlich ist, wenn die Json-Eingabe aus einer unbekannten Quelle stammt (möglicherweise schädliche Shell-Befehle enthält, die eval ausführen kann).

Dies könnte auf folgendes reduziert werden

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

  # print associative array
}

@fedorqui

Wenn Sie Schlüssel und Wert wünschen und basierend auf Wie konvertiere ich ein Json-Objekt in JQ in das Format key = value, können Sie Folgendes tun:

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

Ganz allgemein können Sie die Werte in einem Array myarray[key] = value wie folgt speichern, indem Sie einfach jq der while mit der while ... do; ... done < <(command)-Syntax zur Verfügung stellen:

declare -A myarray
while IFS="=" read -r key value
do
    myarray[$key]="$value"
done < <(jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]" file)

Und dann können Sie die Werte wie folgt durchlaufen:

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

Für diese Eingabe wird Folgendes zurückgegeben:

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

Der Hauptunterschied zwischen dieser Lösung und meiner eigenen besteht darin, das Array In bash oder in jq zu durchlaufen.

Jede Lösung ist gültig und je nach Anwendungsfall kann eine nützlichere Sein als die andere.

3
Sid

So kann es rekursiv gemacht werden:

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

Um es anzurufen:

get_settings "$SETTINGS_JSON"

Auf das Array wird wie folgt zugegriffen:

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

Um das JSON-Objekt effizient in ein assoziatives Bash-Array zu laden (ohne Schleifen in der Bash zu verwenden), kann das Tool 'jq' wie folgt verwendet werden. 

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

Warnung: Dies verwendet eval, was gefährlich ist, wenn die Json-Eingabe aus einer unbekannten Quelle stammt (möglicherweise schädliche Shell-Befehle enthält, die eval ausführen kann).

0
Ján Lalinský