Bill
Bill

Reputation: 33

adding item to json using bash variable with jq

I am trying to use bash to create a string variable that will be used in jq to add new elements to the json file. But it's escaping my double quote. See below for "wrong output" produced. I am expecting the output shown below "expected output". What is the correct way to add more fields to filename with bash variable?

My input json file (input.json):

{
  "##_Comment1": "Inputs",
  "filename": [
    "file1",
    "file2",
    "file3",
    "file4"
  ]
}

My bash script:

#!/bin/bash
update_list='"abc","efg"'

cat input.json | jq --arg args "$update_list" '.["filename"] += [$args]'

wrong output:

{
  "##_Comment1": "Inputs",
  "filename": [
    "file1",
    "file2",
    "file3",
    "file4",
    "\"abc\",\"efg\""
  ]
}

correct output:

{
  "##_Comment1": "Inputs",
  "filename": [
    "file1",
    "file2",
    "file3",
    "file4",
    "abc",
    "efg"
  ]
}

Upvotes: 2

Views: 4599

Answers (2)

peak
peak

Reputation: 116640

In the original question, update_list is a comma-separated listing of quoted strings, and the following solution would work if these strings are also JSON strings:

jq --arg args "$update_list" '
  .["filename"] += ($args|split(",")|map(fromjson))'

update_list variants

If update_list can be made available as a JSON array, then @Attie's first solution would be the way to go.

However if update_list is a bash array, the solution involving one call to jq per array element is unnecessarily (and might be embarrassingly) inefficient; the suggested solution might also create problems because the update is not atomic. There are far better alternatives. For example, the jq FAQ mentions a technique which, when applied to the present problem, yields the following solution:

jq --argjson args "$(printf '%s\n' "${update_list[@]}" | jq -nR '[inputs]')" '
 .["filename"] += $args' 

Or for robustness, one could use NUL as the delimiter:

jq --argjson args "$(printf '%s\0' "${update_list[@]}" | jq -sR 'split("\u0000")')" '
 .["filename"] += $args'

See also jq convert bash array to json array and insert to file

Upvotes: 1

Attie
Attie

Reputation: 6969

Unwind your situation a bit:

$ jq --arg args '"abc","efg"' '.["filename"] += [$args]' <<< '{"filename":[]}'
{
  "filename": [
    "\"abc\",\"efg\""
  ]
}

Here, we are effectively assigning args to a string in the jq engine:

args = "\"abc\",\"efg\""

If you want set args to a list/array, then you'll need to take another approach.


You could either format a JSON argument and use --argjson:

$ jq --argjson args '["abc","efg"]' '.["filename"] += $args' <<< '{"filename":[]}'
{
  "filename": [
    "abc",
    "efg"
  ]
}

Or you could make update_list into an array, and loop:

update_list=()
update_list+=("abc")
update_list+=("efg")

echo '{"filename":[]}' > test

for i in "${update_list[@]}"; do
    jq --arg update_item "${i}" '.["filename"] += [ $update_item ]' < test \
        | sponge test
done
$ cat test
{
  "filename": [
    "abc",
    "efg"
  ]
}

Upvotes: 2

Related Questions