Jq

如何使用jq搜尋和替換數組中的多個值?

  • August 24, 2021

在以下 json 文件中,

{
   "contacts": [
       {
           "name": "John",
           "phone": "1234"
       },
       {
           "name": "Jane",
           "phone": "5678"
       }
   ]
}

我需要根據名稱更新兩個電話號碼並將整個 json 儲存在一個新文件中。

我嘗試過類似的東西:

jq '.contacts[] | select(.name == "John") | .phone = "4321"' < contacts.json >updated_contacts.json

但是後來我不知道如何回到父節點並更改簡的那個,也不知道如何檢索整個 json。

我試圖將根節點儲存在一個變數中as,但它保持不變。

作為臨時解決方法,我只是這樣做:

jq '.contacts[0].number = "4321" | .contacts[1].number = "4321"' < contacts.json >updated_contacts.json

但我不應該依賴數組索引,而是名稱,因為原始 json 可能會改變。

知道如何使用 jq 命令來做到這一點嗎?

要更改一個條目,請確保您使用|=並且該更新運算符的左側是原始文件中的路徑:

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

也就是說,我將名稱放在一個數組中,將新數字放在另一個數組中,然後循環索引並更新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呼叫中更新數字:

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確保如果未列出姓名,則電話號碼保持不變。

引用自:https://unix.stackexchange.com/questions/666033