Modify a key-value in a json using jq in-place
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
Related videos on Youtube
wolfsbane
Updated on July 08, 2022Comments
-
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?
-
Andy about 4 yearsDoes this answer your question? Jq to replace text directly on file (like sed -i)
-
-
willemdh over 4 yearsUnfortunately moreutils / sponge is unavailable on CentOS 8 atm..
-
ahmed_khan_89 over 4 yearsthis worked for a hardcoded string. Is there any solution for a variable i.e $address
-
Pujan over 4 years@ahmed_khan_89 you can use jq '.address = "'${address}'"'
-
chepner over 4 yearsNo, do not interpolate strings into a
jq
filter. Usejq --arg a "$address" '.address = $a'
instead. -
Alexander D almost 4 years@chepner How come you don't recommend interpolating the string? It works when I use Pujan's method
-
codekandis almost 4 yearsWithout
sponge
:echo "$( jq '.address = "abcde"' test.json )" > test.json
-
Flimm over 3 yearsBe careful! Pass the filename as an argument to
sponge
, as in the answer. This is wrong:jq . test.json | sponge > test.json
, you must dojq . test.json | sponge test.json
-
hagemt over 3 yearsIt's an enormous headache to deal with the quoting. Use --arg
-
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 over 3 yearsNotice the file owner and file permission of the
test.json
will be altered due to use ofmv
. I usually usecat
instead. -
chepner over 3 years
mv
is atomic;cat
is not. You can adjust the permissions and owner of the temp file before executingmv
, but preventing another process from accessingtest.json
whilecat
overwrites its contents is more difficult. -
Caleb Faruki over 3 yearsThis 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 about 3 yearsAn additional nod for getting me to look at sponge(1)
-
Lynch almost 3 yearsdo 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 over 2 yearsStreaming 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.