次のjsonファイルから
{
"contacts": [
{
"name": "John",
"phone": "1234"
},
{
"name": "Jane",
"phone": "5678"
}
]
}
名前に基づいて2つの電話番号を更新し、json全体を新しいファイルに保存する必要があります。
私は次のことを試しました:
jq '.contacts[] | select(.name == "John") | .phone = "4321"' < contacts.json >updated_contacts.json
しかし、親ノードに戻ってJaneのノードを変更する方法、またはjson全体を検索する方法がわかりません。
私は変数に保存されたルートノードを試しましたが、as
同じままです。
一時的な解決策として、次のようにしました。
jq '.contacts[0].number = "4321" | .contacts[1].number = "4321"' < contacts.json >updated_contacts.json
ただし、元のjsonが変更される可能性があるため、配列インデックスに依存してはいけません。名前に依存する必要があります。
jqコマンドを使ってこれを行う方法を知っていますか?
答え1
項目を変更するには、|=
更新演算子の左側を使用する必要があります。オリジナル文書:
jq --arg name John --arg phone 4321 \
'( .contacts[] | select(.name == $name) ).phone |= $phone' file
実際には配列から要素セットを抽出する.contacts[] | select(.name == "John") | .phone |= ...
ため、これは使用できません。したがって、文書の主要部分ではなく、抽出された要素のみを変更できます。select()
contacts
違いを確認してください
( ... | select(...) ).phone |= ...
^^^^^^^^^^^^^^^^^^^^^
path in original document
これは有効です。
... | select(...) | .phone |= ...
^^^^^^^^^^^
extracted bits
これはうまくいきません。
たとえば、次のように複数の項目にループを使用しますbash
。
names=( John Jane )
phones=( 4321 4321 )
tmpfile=$(mktemp)
for i in "${!names[@]}"; do
name=${names[i]}
phone=${phones[i]}
jq --arg name "$name" --arg phone "$phone" \
'( .contacts[] | select(.name == $name) ).phone |= $phone' file >"$tmpfile"
mv -- "$tmpfile" file
done
つまり、名前を1つの配列に入れ、新しい番号を別の配列に入れてから、インデックスを繰り返し、一時file
ファイルと中間ストアを使用して変更する必要がある各項目を更新します。
または連想配列を使用します。
declare -A lookup
lookup=( [John]=4321 [Jane]=4321 )
for name in "${!lookup[@]}"; do
phone=${lookup[$name]}
# jq as above
done
次の新しい電話番号を含むJSON入力文書があるとします。
{
"John": 1234,
"Jane": 5678
}
これを使って作成できます
jo John=1234 Jane=5678
jq
その後、1回の通話で番号を更新できます。
jo John=1234 Jane=5678 |
jq --slurpfile new /dev/stdin \
'.contacts |= map(.phone = ($new[][.name] // .phone))' file
これは入力JSONを読み取り、次$new
の構造に新しい数字を入れます。
[
{
"John": 1234,
"Jane": 5678
}
]
これは、通話中にmap()
リストされた連絡先の電話番号を変更するために使用されます。// .phone
名前が記載されていなくても電話番号が変わらないことを確認してください。
答え2
Kusalanandaの回答によると、2つの値のみを検索して置き換えるには、1回のjq呼び出しで次のことを行うことができます。
jq '( .contacts[] | select(.name == "John") ).phone |= "4321" |
( .contacts[] | select(.name == "Jane") ).phone |= "8765"' \
contacts.json
または、チェーン2 jqは次のように呼び出します。
cat contacts.json | \
jq '( .contacts[] | select(.name == "John") ).phone |= "4321"' | \
jq '( .contacts[] | select(.name == "Jane") ).phone |= "8765"'