0

I'm trying to print array data from json in bash and I use jq for this purpose. The problem is, if the value of the record contain spaces, it will messed up the output. Below is the bash script that demonstrates this problem:

This is the full source code (required jq):

#!/bin/bash

# 1) Declare a sample json with 3 records:

json_data=$(cat <<EOF
{
   "records":[
      {
         "name":"record1",
         "value":"\"1.this-is-string-value-of-record-1-with-no-spaces\""
      },
      {
         "name":"record2",
         "value":"\"2. this is string value of record 2 with spaces\""
      },

      {
         "name":"record3",
         "value":"\"3. this is string value of record 3 with spaces\""
      }
   ]
}
EOF
)

# 2) create the first list of array from .name

record=($(echo "${json_data}" | jq -r ".records[].name" | tr '\n' ' '))


# 3) create the second list of array from .value

 record2=($(echo "${json_data}" | jq -r ".records[].value" | tr '\n' ' '))

# 4) printout the array

for i in "${!record[@]}"; do

 echo "The record [ ${record[i]} ] belongs to [ ${record2[i]} ]"

done

Output:

The record [ record1 ] belongs to [ "1.this-is-string-value-of-record-1-with-no-spaces" ]
The record [ record2 ] belongs to [ "2. ]
The record [ record3 ] belongs to [ this ]

Expected output:

The record [ record1 ] belongs to [ 1.this-is-string-value-of-record-1-with-no-spaces ]
The record [ record2 ] belongs to [ 2. this is string value of record 2 with spaces ]
The record [ record3 ] belongs to [ 3. this is string value of record 3 with spaces ]

Could you explain why it does not accept spaces value? Thanks.

Kalib Zen
  • 655
  • 1
  • 5
  • 16
  • Because `$(echo ...)` undergoes field splitting. – oguz ismail Aug 23 '21 at 05:11
  • You mean the array declaration has field splitting ? How to make it accept multiple array in there. Maybe there is alternative without creating spaces `tr '\n' ' ' ` like what I did there. – Kalib Zen Aug 23 '21 at 05:15
  • 2
    Yeah. Populating the array using `readarray` built-in would be much easier for you. Give it a try. – oguz ismail Aug 23 '21 at 05:18
  • 2
    @KalibZen It's not special to array declarations; without double-quotes around it, the result of `$(somecommand)` will be split into "words" (not lines or records or anything useful like that), and then anything that looks like a filename wildcard will be expanded to a list of matching items. This is almost never what you want. And if you double-quote it, it's not split at all. The *right* way to do this is to have `jq` output null-delimited items, and use `readarray -d '' -t` to read them. See chepner's answer [here](https://stackoverflow.com/questions/49321015). – Gordon Davisson Aug 23 '21 at 05:20
  • I'm getting more confused with his answer, so instead of using `tr '\n' ' '` I did this: `readarray -d ' ' -t record2 <<<$(echo "${json_data}" | jq -r ".records[].value")` and doesn't seems it reads the value properly. – Kalib Zen Aug 23 '21 at 05:40
  • 1
    Why `-d ' '`? You want to split the input by newlines, `readarray -t record2 ...` should suffice. – oguz ismail Aug 23 '21 at 06:05
  • @ogus ismail Ok, so we do not need -d ' '. I was having a hard time to understand what Gordon Davisson trying to convey. Thanks both of you.. It's working. But, it's good to have someone wrote the answer, so this question will help other in the future. – Kalib Zen Aug 23 '21 at 06:11

1 Answers1

2

Job with readarray (from bash):

# 2) create the first list of array from .name
readarray -t record < <(echo "${json_data}" | jq -r ".records[].name")

# 3) create the second list of array from .value
readarray -t record2 < <(echo "${json_data}" | jq -r ".records[].value")

# 4) printout the array
for i in "${!record[@]}"; do
    # for removing double quotes
    record2[i]="${record2[i]#\"}"
    record2[i]="${record2[i]%\"}"
    echo "The record [ ${record[i]} ] belongs to [ ${record2[i]} ]"
done

Output:

The record [ record1 ] belongs to [ 1.this-is-string-value-of-record-1-with-no-spaces ]
The record [ record2 ] belongs to [ 2. this is string value of record 2 with spaces ]
The record [ record3 ] belongs to [ 3. this is string value of record 3 with spaces ]

Remarks:

  • readarray works on lines (the output of jq is also). So, don't use -d option
  • use -t option for remove EOL delimiter ('\n')
Arnaud Valmary
  • 2,039
  • 9
  • 14