2

I want to replace the value of "CTE_LG_LL_OE" from "warn" to "ready"

{ "abcd": { "aav": "on", "vvg": "iio7890_APPID", "ct": "b-tte", "eSL": true, "it": "https://%ght%.mjk.com/", "tpo": "i-1", "pd": false, "pm": false, "pr": "" }, "en": { "CTE_LG_LL_OE": "warn", "S_G_EL_OD": "INFO", "G_LG_EL_OVE": "info" }, "EN_HTTPS": false }

I am able to find the same in file using the below command , but how do I replace it with one liner command.

jq '.' /abc/temp/config.json | grep CTE_LG_LL_OE

2 Answers 2

9

To get the key's value, you would use a JSON-aware tool, like jq rather than grep. You would do this for a few reasons:

  1. The value that you are extracting may be encoded. The jq tool would decode this for you if you use it with the -r (--raw-output) option.
  2. Using grep makes no distinction between values and keys, so you may accidentally extract data that you did not plan to extract.

Extracting the value of the CTE_LG_LL_OE key in the top-level en entry:

jq -r '.en.CTE_LG_LL_OE' file

Setting the value to the string ready and writing the resulting document to the file new-file:

jq '.en.CTE_LG_LL_OE |= "ready"' file >new-file

The |= operator is the "update operator" and it takes a "path" to a key to the left and a new value for the key on the right.

To set the value from a shell variable:

jq --arg newval "$newvalue" '.en.CTE_LG_LL_OE |= $newval' file >new-file

This creates a jq variable called $newval from the shell variable newvalue, which we then use in the jq expression. The value in the variable will automatically be JSON-encoded by jq.

For readability, space things out a bit (assuming this is part of a shell script, since the question is tagged with ):

jq --arg newval "$newvalue" \
    '.en.CTE_LG_LL_OE |= $newval' file >new-file

To do in-place editing of the file (jq does not support in-place editing by itself):

tmpfile=$(mktemp)

cp file "$tmpfile" &&
jq --arg newval "$newvalue" \
    '.en.CTE_LG_LL_OE |= $newval' "$tmpfile" >file &&
rm -f "$tmpfile"

As a once-liner:

tmpfile=$(mktemp); cp file "$tmpfile" && jq --arg newval "$newvalue" '.en.CTE_LG_LL_OE |= $newval' "$tmpfile" >file && rm -f "$tmpfile"

If you're not sure of where in the document structure the CTE_LG_LL_OE key is located and just want to update the values of all CTE_LG_LL_OE keys that have the value warn:

jq '
    (
        .. |
        select(type == "object" and .CTE_LG_LL_OE? == "warn").CTE_LG_LL_OE
    ) |= "ready"' file

This examines all keys and values recursively in the whole document. It finds all objects that has a CTE_LG_LL_OE key with th evalue warn and updates these to instead be ready. The newlines are only for readability.

The value could be taken from a shell variable as before:

jq --arg newval "$somevariable" '
    (
        .. |
        select(type == "object" and .CTE_LG_LL_OE? == "warn").CTE_LG_LL_OE
    ) |= $newval' file

This could obviously be combined with doing in-place editing too, as show in the first half of this answer.

3
  • but this is not an one liner command Commented Aug 5, 2021 at 6:26
  • @Alex You mean the last one? Just put the two lines on the same line if you want: newvalue=ready; jq --arg newval "$newvalue" '...' etc. Note that the assignment to the shell variable newvalue is irrelevant to the actual updating of the value in the JSON file (which is done on a single line already). That value can be a pre-existing shell variable value, something read from a user, or the output from some other program. Commented Aug 5, 2021 at 6:29
  • 3
    @Alex Almost any shell command, no matter how complicated, can be written on a single line, if you're happy writing unreadable code. Also, why would you ever want to write "one-liners" in a shell script? Commented Aug 5, 2021 at 6:30
3

With your example content in file.json, the following one-liner works:

cat file.json | jq '.en.CTE_LG_LL_OE = "ready"' | tee file.json

To break this down:

  1. cat … copies the contents of file.json to standard output (stdout).
  2. | sends this output to the standard input (stdin) of the next command.
  3. jq '.en.CTE_LG_LL_OE = "ready"' has what jq calls a ‘filter’ ('.en.CTE_LG_LL_OE = "ready"', assigning that specific value to that specific key) but does not specify any input file(s). This causes jq to read its input from stdin.
  4. | tee sends the output from jq to the tee command, which writes it to both stdout and the named file.

I found this question after being puzzled by the behaviour of jq myself. For some reason (compare answers at https://stackoverflow.com/questions/48964305/write-output-to-a-file-after-piped-to-jq) output redirection does not work, nor does having jq do the file read itself. In short, none of the following work; any of them will truncate file.json, leaving it totally empty:

jq '.en.CTE_LG_LL_OE = "ready"' file.json >file.json

jq '.en.CTE_LG_LL_OE = "ready"' file.json | tee file.json

cat file.json | jq '.en.CTE_LG_LL_OE = "ready"' >file.json

It's possible some of the standard command-line options would alter this behaviour.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.