Modify a key-value in a json using jq in-place

97,154

Solution 1

AFAIK jq does not support in-place editing, so you must redirect to a temporary file first and then replace your original file with it, or use sponge utility from the moreutils package, like that:

jq '.address = "abcde"' test.json|sponge test.json

There are other techniques to "redirect to the same file", like saving your output in a variable e.t.c. "Unix & Linux StackExchange" is a good place to start, if you want to learn more about this.

Solution 2

Use a temporary file; it's what any program that claims to do in-place editing is doing.

tmp=$(mktemp)
jq '.address = "abcde"' test.json > "$tmp" && mv "$tmp" test.json

If the address isn't hard-coded, pass the correct address via a jq argument:

address=abcde
jq --arg a "$address" '.address = $a' test.json > "$tmp" && mv "$tmp" test.json

Solution 3

Temp files add more complexity when not needed (unless you are truly dealing with JSON files so large you cannot fit them in memory (GB to 100's of GB or TB, depending on how much RAM/parallelism you have)

The Pure bash way.

contents="$(jq '.address = "abcde"' test.json)" && \
echo -E "${contents}" > test.json

Pros

  • No temp file to juggle
  • Pure bash
  • Don't need an admin to install sponge, which is not installed by default
  • Simpler

Cons

  • This works perfectly fine for json because it cannot contain a literal null character. If you were to try this outside the json arena, it would fail when a null is encountered (and you would have to do some encoding/decoding workarounds). Bash variables cannot store literal nulls.

Note: this can not be combined as "one command" (like @codekandis suggested), since redirection sometimes starts before the left hand side (LHS) of an expression is run, and starting redirection before running jq erroneously empties the file, hence two separate commands. It may "seem" to work when you try it, but this is misleading and has a very high probability of failing as soon as the circumstances change.

Update: Added -E option to disable escape characters just in case you are on systems where they are interpreted by default. (Which I've never actually seen)

Solution 4

Just to add to chepner answer and if you want it in a shell script.

test.json

{
  "name": "abcd",
  "age": 30,
  "address": "abc"
}

script.sh

#!/bin/bash
address="abcde"
age=40

# Strings:
jq --arg a "${address}" '.address = $a' test.json > "tmp" && mv "tmp" test.json

# Integers:
jq --argjson a "${age}" '.age = $a' test.json > "tmp" && mv "tmp" test.json

Solution 5

Example for nested json with changing single and multiple values.

config.json

{
  "Parameters": {
    "Environment": "Prod",
    "InstanceType": "t2.micro",
    "AMIID": "ami-02d8e11",
    "ConfigRegion": "eu-west-1"
  }
}

with the below command, you can edit multiple values.

tmp=$(mktemp)
jq '.Parameters.AMIID = "ami-02d8sdfsdf" | .Parameters.Environment = "QA"' config.json > "$tmp" && mv "$tmp" config.json

with the below command, you can edit single value.

tmp=$(mktemp)
jq '.Parameters.AMIID = "ami-02d8sdfsdf"' config.json > "$tmp" && mv "$tmp" config.json
Share:
97,154

Related videos on Youtube

wolfsbane
Author by

wolfsbane

Updated on July 08, 2022

Comments

  • wolfsbane
    wolfsbane almost 2 years

    I have a json in which I want to modify a particular value but the terminal always displays the json with the modified value but it does not actually change the value in the particular file. Sample json:

    {
       name: 'abcd',
       age: 30,
       address: 'abc'
    }
    

    I want to change the value of address in the file itself but so far I've been unable to do so. I tried using:

    jq '.address = "abcde"' test.json
    

    but it didn't work. Any suggestions?

  • willemdh
    willemdh over 4 years
    Unfortunately moreutils / sponge is unavailable on CentOS 8 atm..
  • ahmed_khan_89
    ahmed_khan_89 over 4 years
    this worked for a hardcoded string. Is there any solution for a variable i.e $address
  • Pujan
    Pujan over 4 years
    @ahmed_khan_89 you can use jq '.address = "'${address}'"'
  • chepner
    chepner over 4 years
    No, do not interpolate strings into a jq filter. Use jq --arg a "$address" '.address = $a' instead.
  • Alexander D
    Alexander D almost 4 years
    @chepner How come you don't recommend interpolating the string? It works when I use Pujan's method
  • codekandis
    codekandis almost 4 years
    Without sponge: echo "$( jq '.address = "abcde"' test.json )" > test.json
  • Flimm
    Flimm over 3 years
    Be careful! Pass the filename as an argument to sponge, as in the answer. This is wrong: jq . test.json | sponge > test.json , you must do jq . test.json | sponge test.json
  • hagemt
    hagemt over 3 years
    It's an enormous headache to deal with the quoting. Use --arg
  • chepner
    chepner over 3 years
    @AlexanderD Because interpolation doesn't necessarily pass an argument to your filter; it builds a filter, and that filter depends on how the variable expansion is parsed.
  • VCD
    VCD over 3 years
    Notice the file owner and file permission of the test.json will be altered due to use of mv. I usually use cat instead.
  • chepner
    chepner over 3 years
    mv is atomic; cat is not. You can adjust the permissions and owner of the temp file before executing mv, but preventing another process from accessing test.json while cat overwrites its contents is more difficult.
  • Caleb Faruki
    Caleb Faruki over 3 years
    This answer is particularly useful for things such as bumping the version on your package.json (e.g. contents="$(jq '.version = "$version"' package.json)" && echo "${contents}" > package.json)
  • sberder
    sberder about 3 years
    An additional nod for getting me to look at sponge(1)
  • Lynch
    Lynch almost 3 years
    do not redirect to the same file as suggested by @codekandis comment. this will not always work. Large files it will cause issues, also with whitespaces, non-printable and escapment sequences. Never redirect a file to itself, it is always a bad idea. See sponge or use a temp file, just don't try to do it differently unless you understand what is happening.
  • user1010997
    user1010997 over 2 years
    Streaming the result into the input file is generally risky in shell scripts: > normally causes the output file to be truncated before the rest of the command is executed. Possibly the subshell command $(...) postpones this until after the execution of the shell script. In general I would always use a command line option to deal with this special case, or lacking that, write to an intermediate file.