web-dev-qa-db-fra.com

Jq pour remplacer le texte directement sur le fichier (comme sed -i)

J'ai un fichier JSON qui doit être mis à jour dans certaines conditions. 

Échantillon json

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

J'écris un script qui utilise Jq pour faire correspondre une valeur et mettre à jour, comme indiqué ci-dessous

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

Sortie (imprimée sur le terminal)

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

Bien que cette commande apporte les modifications nécessaires, elle affiche l'intégralité du JSON sur le terminal et ne modifie pas le fichier lui-même. 

Veuillez indiquer s’il existe une option permettant à jq d’apporter des modifications directement sur le fichier (similaire à sed -i).

16
Supra

Cet article aborde la question de l'absence d'équivalent de l'option "-i" de sed, et en particulier de la situation décrite:

J'ai un tas de fichiers et écrire chacun dans un fichier séparé ne serait pas facile. 

Il existe plusieurs options, du moins si vous travaillez sur un Mac, Linux ou un environnement similaire. Leurs avantages et inconvénients sont discutés à l'adresse http://backreference.org/2011/01/29/in-place-editing-of-files/ Je vais donc me concentrer sur trois techniques seulement. :

La première consiste simplement à utiliser "&&" dans le sens suivant:

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

Une autre consiste à utiliser l'utilitaire sponge (partie de GNU moreutils):

jq ... INPUT | sponge INPUT

La troisième option peut être utile s'il est avantageux d'éviter de mettre à jour un fichier s'il n'y a aucune modification. Voici un script qui illustre une telle fonction:

#!/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

Vous voudrez mettre à jour les objets d'action sans changer le contexte. En ayant le tuyau là-bas, vous modifiez le contexte pour chaque action individuelle. Vous pouvez contrôler cela avec des parenthèses.

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

Cela devrait donner:

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

Vous pouvez rediriger les résultats vers un fichier pour remplacer le fichier d'entrée. Il ne fera pas les mises à jour sur place d'un fichier comme le fait sed.

5
Jeff Mercado

En utilisant ma réponse à une question en double

Assignment imprime l'intégralité de l'objet avec l'affectation exécutée afin que vous puissiez affecter une nouvelle valeur à .Actions du tableau Actions modifié.

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

J'ai utilisé une instruction if mais nous pouvons utiliser votre code pour faire la même chose

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

Ce qui précède produira l’ensemble du json avec .Actions édité . Jq n’a pas de fonctionnalité semblable à sed -i, mais tout ce que vous avez à faire est de le rediriger dans un fichier sponge vers le fichier avec | sponge

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