Как искать и заменять несколько значений в массиве с помощью jq?

Если у вас есть perl переименование Ларри Уолла

rename 's/[.]jpeg[0-9]*/.jpeg/' *.jpeg[0-9]*

если у вас есть только переименование Util -Linux, это не сработает.

Вы можете использовать цикл for в bash

for n in *.jpeg[0-9]*
do mv -i "$n" "${n%%.jpeg[0-9]*}.jpeg"
done

${var%%pattern}представляет собой расширение стандартного параметра , которое расширяется до значения varс удалением (самой длинной )замыкающей части, соответствующей pattern. Итак, если n— это foo.jpeg123, ${n%%.jpeg[0-9*}— это foo.(foo.jpeg1abcтакже приведет к foo.)

0
24.08.2021, 09:10
2 ответа

Чтобы изменить одну запись, убедитесь, что вы используете |=и что левая часть этого оператора обновления является путем в исходном документе:

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гарантирует, что если имя не указано в списке, номер телефона останется прежним.

1
24.08.2021, 10:08

Основываясь на ответе Кусалананды, если вы хотите найти и заменить только 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"'
0
24.08.2021, 12:46

Теги

Похожие вопросы