다음 json 파일에서
{
"contacts": [
{
"name": "John",
"phone": "1234"
},
{
"name": "Jane",
"phone": "5678"
}
]
}
이름을 기준으로 두 개의 전화번호를 업데이트하고 전체 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
즉, 이름을 하나의 배열에 넣고 새 숫자를 다른 배열에 넣은 다음 인덱스를 반복하고 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
이름이 기재되어 있지 않더라도 전화번호가 동일하게 유지되는지 확인하세요 .
답변2
Kusalananda의 답변에 따르면 2개의 값만 검색하고 바꾸려면 한 번의 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"'