6

jqJSONファイルを解析し、一連の各JSON配列をシェル配列に抽出するために使用しています。

私の現在のコードは次のようになります。

for ((i = 0; i < ${#nvars[@]}; i++)); do
    v1=($(cat $INPUT | jq '."config"[i]."var1"[]'))
    echo $v1
done

エラーメッセージ:

error: i is not defined

私も交換しました

v1=($(cat $INPUT | jq '."config"[i]."var1"[]'))

v1=($(cat $INPUT | jq '."config"[$i]."var1"[]'))

まだ動作していません。何か案が?どんな助けでも大歓迎です!


編集: サンプル入力データ

{
    "config-vars":[
        {
            "var1":["v1","v2"],
            "var2":""
        },
        {
            "var1":["v3",""],
            "var2":"v4"
        }
    ]
}
4

4 に答える 4

15

改善の余地はかなりあります。ここから始めましょう:

v1=($(cat $INPUT | jq '."config"[$i]."var1"[]'))

...まず、実際に使用する必要はありませんcat; jq入力ファイルから直接ではなく、パイプから読み取ることを強制するため、パフォーマンスが低下します。実行jq <"$INPUT"するだけでより堅牢になります(または、より良いのは<"$input"、シェルの組み込み関数と環境変数のために慣例で予約されているすべて大文字の名前の使用を避けるためです)。

次に、入力ファイル名の展開を含め、すべての変数展開を引用符で囲む必要があります。そうしないと、ファイル名にスペースが含まれるたびにバグが発生します。

3 番目に、IFS 内array=( $(stuff) )のすべての文字の出力をstuff分割し、その分割の結果を一連のグロブ式として展開します (したがって、出力に が含まれていて、テキスト ファイルを含むディレクトリでこのスクリプトを実行している場合は、結果配列内のそれらのファイルの名前)。改行のみで分割すると、複数単語の文字列を正しく解析できることを意味し、グロブ文字が存在する場合にこの手法を確実に使用するには、グロブ展開を無効にする必要があります。これを行う 1 つの方法は、このコマンドを実行する前に設定して実行することです。もう 1 つは、コマンドの出力をループにリダイレクトすることです (以下を参照)。*.txtIFS=$'\n'set -hwhile read

第 4 に、コードへの文字列置換は、どの言語でも悪い習慣です。その方法は (ローカルで同等の) Bobby Tablesであり、実行可能コードとして処理されるコンテンツを提供するために、プロセスに渡されたデータのみを変更できるはずの誰かを許可します。 (ただし、この場合はjqスクリプトとして、より完全な機能を備えた言語で任意のコードを実行するよりも危険性が低くなりますが、それでも、これにより余分なデータが出力に追加される可能性があります)。

次に、jq改行で区切られたコンテンツを発行するようになったら、それを配列に読み込む必要はまったくありません。コンテンツをシェルから書き込んjqだり、シェルに読み込んだりするときに、コンテンツを反復処理できるため、シェルが必要とするのを防ぐことができます。そのコンテンツをバッファリングするためにメモリを割り当てるには:

while IFS= read -r; do
  echo "read content from jq: $REPLY"
done < <(jq -r --arg i "$i" '.config[$i | tonumber].var1[]' <"$input")

最後に、配列を操作したいとしましょう。これを行うには、落とし穴を回避する 2 つの方法があります。1 つは、IFS明示的に設定し、割り当ての前にグロブ展開を無効にすることです。

IFS=$'\n' # split only on newlines
set -f
result=( $(jq -r ... <"$input") )

もう 1 つは、ループを使用して配列に割り当てることです。

result=( )
while IFS= read -r; do
  result+=( "$REPLY" )
done < <(jq -r ... <"$input")

...または、@JohnKugelman で提案されているようread -aに、1 回の操作で配列全体を読み取るために使用します。

IFS=$'\n' read -r -d '' -a result < <(jq -r ... <"$input")
于 2015-01-16T22:30:20.683 に答える
3

変数は単一引用符内では補間されません。代わりに二重引用符を使用し、既存の引用符を削除してください。

v1=($(cat $INPUT | jq ".config[$i].var1[]"))

または、--argオプションを使用すると、一重引用符を使用できます。

v1=($(cat $INPUT | jq --arg i "$i" '.config[$i].var1[]'))

cat の無駄な使用を修正することもできます。

v1=($(jq ".config[$i].var1[]" "$INPUT"))

また、@CharlesDuffy の回答を参照して、このような配列への割り当てが安全ではない理由の優れた詳細な説明を参照してください。

于 2015-01-16T22:01:37.937 に答える