web-dev-qa-db-ja.com

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

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

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

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

例:

  • URL = "example.com"
  • 著者= "John Doe"
  • CREATED = "2017年10月22日"

これまでに取得したものは配列を反復処理しますが、文字列を作成します。

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か何かが必要だと思いますが、指を置いていないようです。

19
EHerman

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

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

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

出力:

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

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

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

この文字列は、_`_、_$_、改行、およびnull文字がデータに表示されない限り、evalableです。

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

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

29
Michael Homer

@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'
13
cas

結果をループして各反復を評価できることに気づきました:

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
1
EHerman

私は@Michelの提案が本当に好きです。場合によっては、BASHを使用して特定のサーバーでタスクを実行するために、いくつかの変数の値を抽出することがあります。したがって、必要な変数はわかっています。このアプローチを使用することは、変数ごとに値を設定するためのjqの回避または複数の呼び出し、または複数の変数でreadステートメントを使用する方法であり、いくつかは有効で空であり、値のシフトを引き起こします(これは私の問題でした)

。svID [] .ID = ""(svslotIDvalue

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

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

evalとフィルターを使用すると、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 )"  

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

0
Thiago Conrado