Jq to replace text directly on file (like sed -i) Jq to replace text directly on file (like sed -i) bash bash

Jq to replace text directly on file (like sed -i)


This post addresses the question about the absence of the equivalent of sed's "-i" option, and in particular the situation described:

I have a bunch of files and writing each one to a separate file wouldn't be easy.

There are several options, at least if you are working in a Mac or Linux or similar environment. Their pros and cons are discussed athttp://backreference.org/2011/01/29/in-place-editing-of-files/so I'll focus on just three techniques:

One is simply to use "&&" along the lines of:

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

Another is to use the sponge utility (part of GNU moreutils):

jq ... INPUT | sponge INPUT

The third option might be useful if it is advantageous to avoid updating a file if there are no changes to it. Here is a script which illustrates such a function:

#!/bin/bashfunction maybeupdate {    local f="$1"    cmp -s "$f" "$f.tmp"    if [ $? = 0 ] ; then      /bin/rm $f.tmp    else      /bin/mv "$f.tmp" "$f"    fi}for fdo    jq . "$f" > "$f.tmp"    maybeupdate "$f"done


instead of sponge :

cat <<< $(jq 'QUERY' sample.json) > sample.json


You'll want to update the action objects without changing the context. By having the pipe there, you're changing the context to each individual action. You can control that with some parentheses.

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

This should yield:

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

You can redirect the results to a file to replace the input file. It won't do in-place updates to a file as sed does.