it-swarm.com.ru

Jq для замены текста непосредственно в файле (например, sed -i)

У меня есть файл JSON, который должен быть обновлен при определенных условиях. 

Образец JSON

{
   "Actions" : [
      {
         "value" : "1",
         "properties" : {
            "name" : "abc",
            "age" : "2",
            "other ": "test1"
          }
      },
      {
         "value" : "2",
         "properties" : {
            "name" : "def",
            "age" : "3",
            "other" : "test2"
          }
      }
   ]
}

Я пишу скрипт, который использует Jq для сопоставления значения и обновления, как показано ниже

cat sample.json |  jq '.Actions[] | select (.properties.age == "3") .properties.other = "no-test"'

Вывод (распечатывается на терминал)

{
  "value": "1",
  "properties": {
    "name": "abc",
    "age": "2",
    "other ": "test1"
  }
}
{
  "value": "2",
  "properties": {
    "name": "def",
    "age": "3",
    "other": "no-test"
  }
}

Хотя эта команда вносит необходимые изменения, она выводит весь json на терминал и не вносит изменений в сам файл. 

Пожалуйста, сообщите, если есть возможность, чтобы jq вносил изменения в файл напрямую (аналогично sed -i).

16
Supra

В этом посте рассматривается вопрос об отсутствии эквивалента опции sed "-i" и, в частности, описанная ситуация:

У меня есть куча файлов, и записать каждый в отдельный файл будет нелегко. 

Есть несколько вариантов, по крайней мере, если вы работаете в Mac или Linux или аналогичной среде. Их плюсы и минусы обсуждаются по адресу http://backreference.org/2011/01/29/in-place-editing-of-files/ , Поэтому я сосредоточусь только на трех методах :

Одним из них является просто использование «&&» в соответствии с:

jq ... INPUT > INPUT.tmp && mv INPUT.tmp INPUT

Другой способ - использовать утилиту sponge (часть GNU moreutils):

jq ... INPUT | sponge INPUT

Третий вариант может быть полезен, если полезно избегать обновления файла, если в нем нет изменений. Вот скрипт, который иллюстрирует такую ​​функцию:

#!/bin/bash

function maybeupdate {
    local f="$1"
    cmp -s "$f" "$f.tmp"
    if [ $? = 0 ] ; then
      /bin/rm $f.tmp
    else
      /bin/mv "$f.tmp" "$f"
    fi
}

for f
do
    jq . "$f" > "$f.tmp"
    maybeupdate "$f"
done
14
peak

Вы хотите обновить объекты действия без изменения контекста. Имея канал, вы меняете контекст для каждого отдельного действия. Вы можете контролировать это с помощью некоторых скобок.

$ jq --arg age "3" \
'(.Actions[] | select(.properties.age == $age).properties.other) = "no-test"' sample.json

Это должно привести к:

{
  "Actions": [
    {
      "value": "1",
      "properties": {
        "name": "abc",
        "age": "2",
        "other ": "test1"
      }
    },
    {
      "value": "2",
      "properties": {
        "name": "def",
        "age": "3",
        "other": "no-test"
      }
    }
  ]
}

Вы можете перенаправить результаты в файл, чтобы заменить входной файл. Он не будет выполнять обновления файла на месте, как это делает sed.

5
Jeff Mercado

Используя мой ответ на дублирующий вопрос

Присвоение печатает весь объект с выполненным присваиванием, чтобы вы могли присвоить новое значение .Actions измененного массива Actions

.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])

Я использовал оператор if, но мы можем использовать ваш код, чтобы сделать то же самое

.Actions=[.Actions[] | select (.properties.age == "3").properties.other = "no-test"]

Выше будет выводить весь json с отредактированным .Actions . Jq не имеет sed -i-подобной функциональности, но все, что вам нужно сделать, это перенаправить его обратно в sponge в файл с | sponge

 jq '.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])' sample.json | sponge sample.json
0
Will Barnwell