jqを使用して変数をbashするJSON配列


18

私はそのようなJSON配列を持っています:

{
  "SITE_DATA": {
    "URL": "example.com",
    "AUTHOR": "John Doe",
    "CREATED": "10/22/2017"
  }
}

jqを使用してこの配列を反復処理して、各項目のキーを変数名として、値を値として設定できるようにしたいと考えています。

例:

  • URL = "example.com"
  • AUTHOR = "John Doe"
  • CREATED = "10/22/2017"

これまでのところ、配列を反復処理しますが、文字列を作成します。

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

どの出力:

URL=example.com
AUTHOR=John Doe
CREATED=10/22/2017

これらの変数をスクリプトのさらに下で使用したいと考えています。

echo ${URL}

しかし、これは現時点では空の出力をエコーし​​ます。私はevalそこに何かが必要だと思いますが、指をその上に置くことができないようです。

回答:


28

eval著者名にはスペースが含まれているため、元のバージョンは使用できません。Doe環境変数をにAUTHOR設定してコマンドを実行すると解釈されJohnます。またjq、それ自体にパイプする必要はほとんどありません。内部のパイピングとデータフローは異なるフィルターを接続できます。

jqプログラムのはるかに単純なバージョンを作成できます。

jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""'

出力:

URL="example.com"
AUTHOR="John Doe"
CREATED="10/22/2017"

map:の必要はありません。.[]パイプラインの残りの部分を介して配列内の各オブジェクトを個別のアイテムとして取得するため、最後以降のすべてのオブジェクトが|それぞれに個別に適用されます。最後に+、値を囲む引用符を含め、通常の連結で有効なシェル割り当て文字列を組み立てます。

ここではすべてのパイプが重要です。パイプがないと、かなり役に立たないエラーメッセージが表示され、プログラムの一部が微妙に異なるコンテキストで評価されます。

この文字列はeval長い文字などのようことができ`$改行とnullは、データには表示されません。

eval "$(jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""' < data.json)"
echo "$AUTHOR"

これまでどおり、を使用evalするときは、取得するデータを信頼するように注意してください。悪意のあるデータや予期しない形式の場合は、事態が非常に悪くなる可能性があります。


13

@Michael Homerの答えにeval基づいて、データを連想配列に読み込むことにより、潜在的に安全でない可能性を完全に回避できます。

たとえば、JSONデータが次のファイルにある場合file.json

#!/bin/bash

typeset -A myarray

while IFS== read -r key value; do
    myarray["$key"]="$value"
done < <(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + .value ' file.json)

# show the array definition
typeset -p myarray

# make use of the array variables
echo "URL = '${myarray[URL]}'"
echo "CREATED = '${myarray[CREATED]}'"
echo "AUTHOR = '${myarray[URL]}'"

出力:

$ ./read-into-array.sh 
declare -A myarray=([CREATED]="10/22/2017" [AUTHOR]="John Doe" [URL]="example.com" )
URL = 'example.com'
CREATED = '10/22/2017'
AUTHOR = 'example.com'

1
あなただけでは間接譲渡もできたdeclare -- “$key=$value”し、持って$AUTHOR配列することなく、元のようになどの作業を。evalよりも安全ですが、変更PATHや何かがまだ可能であるため、このバージョンよりも安全ではありません。
マイケル・ホーマー

1
ええ、配列は変数を選択したコンテナーにうまく分離します-重要な環境変数を誤って/悪意を持っていじる可能性はありません。declare --$ keyを許可された変数名のリストと比較することにより、バージョンを安全にすることができます。
cas

1

結果をループし、各反復を評価できることに気付いただけです。

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

for key in ${constants}; do
  eval ${key}
done

私にできること:

echo ${AUTHOR}
# outputs John Doe

0

@Michelの提案が本当に好きです。場合によっては、BASHを使用して特定のサーバーでタスクを実行するために、いくつかの変数の値を抽出することがあります。したがって、目的の変数はわかっています。これは、このアプローチを使用して、変数ごとに値を設定するためのjqへの複数の呼び出しを回避したり、複数の変数を使用してreadステートメントを使用したりすることができます。

.svID [] .ID = ""(svslotID値を取得する場合、リードする私の以前のアプローチは値シフトエラーを引き起こします

-rd '\n' getInfo sv slotID <<< $(jq -r '(.infoCMD // "no info"), (.svID[].ID // "none"), (._id // "eeeeee")' <<< $data)

curlを使用してオブジェクトをダウンロードした場合、データ配列からデータを抽出するために、いくつかの変数の名前をわかりやすい名前に変更する私のアプローチは次のとおりです

evalとfiltersを使用すると、1行で問題が解決され、目的の名前の変数が生成されます

eval "$(jq -r '.[0] | {varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

この場合の利点は、最初のステップで必要なすべての変数をフィルタリング、名前変更、フォーマットすることです。があることを確認してください。[0] | これは、ソースがGETを使用したRESTFULL APIサーバーからの場合、応答データが次のような場合に非常に一般的です。

[{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}]

あなたのデータが配列からのものでない場合、すなわち。次のようなオブジェクトです:

{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}

最初のインデックスを削除するだけです:

eval "$(jq -r '{varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

これは古い質問ですが、見つけるのが難しかったので共有を感じました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.