次のJSON配列があります。
{
"SITE_DATA": {
"URL": "example.com",
"AUTHOR": "John Doe",
"CREATED": "10/22/2017"
}
}
各項目のキーを変数名に設定し、値をその値に設定できるように、jqを使用してこの配列を繰り返したいと思います。
例:
- URL="example.com"
- 著者 = "ジョン・ドー"
- 作成済み="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
手に握ることができないようです。
答え1
作成者名にスペースが含まれているため、元のバージョンは実行されません。環境変数がに設定されたコマンドをeval
実行すると解釈されます。実際に独自にパイプする必要もありません。Doe
AUTHOR
John
jq
内部パイプラインとデータフローさまざまなフィルタを一緒に接続できます。
これはすべて入力データを完全に信頼する場合にのみ意味があります(例えば、制御するツールによって生成されたデータ)。いくつかの考えられる問題を以下に詳細に説明するが、データ自体は確かに現在予想される形式であると仮定する。
jqプログラムのより簡単なバージョンを作成できます。
jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + (.value | @sh)'
出力:
URL='example.com'
AUTHOR='John Doe'
CREATED='10/22/2017'
不要map
:.[]
パイプラインの残りの部分を通して、配列内の各オブジェクトを別々の項目として扱います。したがって、最後の項目以降のすべての内容は|
それぞれに個別に適用されます。最後に、+
適切な文字列を含む一般的な接続を使用して、有効なシェル割り当て文字列を単純に組み合わせます。値を引用してエスケープします。@sh
。
ここでは、すべてのパイプが重要です。パイプが存在しない場合は、プログラムのさまざまな部分が微妙に異なるコンテキストで評価される無駄なエラーメッセージが表示されます。
この文字列eval
は入力データを完全に信頼する場合希望の効果を得る。
eval "$(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + (.value | @sh)' < data.json)"
echo "$AUTHOR"
いつものようにeval
、データが悪意のあるまたは予期しない形式の場合、重大なエラーが発生する可能性があるため、信頼できるデータに注意してください。特に、鍵$
実行中のコマンドを生成するシェルメタ文字(または空白など)を含みます。たとえば、PATH
誤って環境変数を上書きすることがあります。
データが信頼できない場合は、この操作をまったく実行しないか、まず目的のキーのみを含むようにオブジェクトをフィルタリングしてください。
jq '.SITE_DATA | { AUTHOR, URL, CREATED } | ...'
次のような場合でも問題が発生する可能性があります。値配列なので.value | tostring | @sh
もっといいでしょう。ただし、この警告リストは良い理由になる可能性があります。いいえこれらすべてを最初に行います。
以下を生成することもできます。連想配列代わりに、キーと値の両方が引用されます。
eval "declare -A data=($(jq -r '.SITE_DATA | to_entries | .[] | @sh "[\(.key)]=\(.value)"' < test.json))"
その後、${data[CREATED]}
キーや値の内容が何であれ、作成日などが含まれます。これは最も安全なオプションですが、エクスポートできる最上位レベルの変数は生成されません。値が配列の場合、まだBash構文エラーが発生する可能性があります。オブジェクトの場合、まだjqエラーが発生する可能性がありますが、コードが実行されたり何も上書きされません。
答え2
@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'
答え3
結果を繰り返し、各繰り返しを評価できることに気づきました。
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
答え4
@Michelの提案が本当に好きです。場合によっては、特定のサーバーでタスクを実行するためにBashを使用するために実際にいくつかの変数値を抽出することがあります。したがって、必要な変数が既知の場合、このアプローチを使用すると、各jq
変数の値を設定するための複数の呼び出しを回避できます。あるいは、read
複数の変数を含むステートメントを使用することもできます。そのうちのいくつかは有効または空になる可能性があるため、値が移動します(例:私の問題)。
以前のアプローチでは、値変換エラーが発生しました。 .svID[ ].ID="" の場合、sv
次のエラーが発生しました。スロット番号値。
-rd '\n' getInfo sv slotID <<< $(jq -r '(.infoCMD // "no info"), (.svID[].ID // "none"), (._id // "eeeeee")' <<< $data)
カールを使用してオブジェクトをダウンロードする場合、データ配列からデータを抽出するためにいくつかの変数の名前を使い慣れた名前に変更する方法は次のとおりです。
eval と filter を使用する行が 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 を使用する RESTful 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 )"