47

I'm using curl to get JSON back from a rest api like this:

content=$(curl -s  -X GET -H "Header:Value" http://127.0.0.1:8200/etc)
echo "${content}"| jq -r '.data.value'

which produces the value I need. However; when I change the above code to look like this:

content=$(curl -s  -X GET -H "Header:Value" http://127.0.0.1:8200/etc)
username=$(echo "${content}"| jq -r '.data.value')
echo $username

Produces nothing. How can I change this so that the username variable gets assigned the output?

jymbo
  • 955
  • 1
  • 7
  • 7
  • 1
    Consider showing the contents of the `$content` variable. – Kusalananda Feb 11 '21 at 16:47
  • I've closed this as the code in your [self-answer](https://unix.stackexchange.com/a/354951/116858) is essentially the same as in the question. The code is therefore _unlikely_ the reason why this failed for you. It's more likely that there is something strange going on with the actual data being passed from the web request (it's possibly being changed by `echo`, which may interpret back-slash sequences). We need more information about the actual data before being able to properly describe what's wrong and what to do about it. – Kusalananda Feb 12 '21 at 08:43

4 Answers4

36

Changed the code to this and it worked:

content=$(curl -s  -X GET -H "Header:Value" http://127.0.0.1:8200/etc) 
username=$( jq -r  '.data.value' <<< "${content}" ) 
echo "${username}"
Kusalananda
  • 320,670
  • 36
  • 633
  • 936
jymbo
  • 955
  • 1
  • 7
  • 7
  • 6
    There is no indication as to _why_ the user's code doesn't work. Your code here is functionally identical, at least at first glance. So, what's the difference that makes it work? – Kusalananda Feb 13 '21 at 14:26
6

You can convert it to a one liner as:

username=$( curl -s  -X GET -H "Header:Value" http://127.0.0.1:8200/etc |  jq -r  '.data.value' ) 
echo ${username}
ShahNewazKhan
  • 161
  • 1
  • 2
5

This only works if you lose the "echo" on the second line.

content=$(curl -s  -X GET -H "Header:Value" http://127.0.0.1:8200/etc) 
username=$( echo jq -r  '.data.value' <<< "${content}" ) 
echo ${username}
Kusalananda
  • 320,670
  • 36
  • 633
  • 936
Anonymous
  • 67
  • 1
  • 2
    This is just a repeat of [an earlier answer](https://unix.stackexchange.com/a/354951/116858). I edited that other answer so that it's correct. This answer still contains the errors (`echo` in the command substitution, and no quoting of `$username` on last line). – Kusalananda Feb 13 '21 at 14:25
2

You can use the bash builtin read together with a process substitution connecting the output of jq to a FIFO:

read username < <(curl -s -X GET -H "Header:Value" http://127.0.0.1:8200/etc | jq -r '.data.value')

As long as you only need to assign a single jq value, the other proposed solutions are simplier. The present solution with read is useful when you need to assign multiple keys to multiple shell variables in one shot; you then just need to use read var1 var2 var3 < <(...).

Here are more general snippets to assign jq output to shell variables, that can be very handy.

Two methods for single-line input (without loop):

IFS=$'\n' read -rd '' a b < <(echo '{"a":"1","b":"2"}' | jq -r '(.a, .b)'); declare -p a b
  declare -- a="1"
  declare -- b="2"

read a b < <(echo '{"a":"1","b":"2"}' | jq -r '[.[]] | @tsv'); declare -p a b
  declare -- a="1"
  declare -- b="2"

Note1: @tsv uses space delimiter; for values that may have spaces you can use the @csv format. Note2: IFS=$'\n' read -rd '' a b < is assuming that no output lines are empty. If some json keys are empty, you can then use 'jq' without '--raw' and remove the "'s afterwards with a=${a:1:-1}, or you can use readarrayinstead of read (cf. example below).

Three methods for multi-lines input (with loop):

echo '[{"a":"1","b":"2"},{"a":"3","b":"4"}]' |
  jq -r '.[] | {a, b} | [.[]] | @csv' |
  while IFS= read -r i; do
    IFS=, read -r a b <<< "$i"; a=${a:1:-1}; b=${b:1:-1}; declare -p a b;
  done

  declare -- a="1"
  declare -- b="2"
  declare -- a="3"
  declare -- b="4"

echo '[{"a":"1","b":"2"},{"a":"3","b":"4"}]' |
  jq -r '.[] | (.a, .b)' |
  while IFS= read -r a; do
    IFS= read -r b; declare -p a b;
  done

  declare -- a="1"
  declare -- b="2"
  declare -- a="3"
  declare -- b="4"

echo '[{"a":"1","b":"2"},{"a":"3","b":"4"}]' |
  jq -r '.[] | (.a, .b)' |
  while readarray -n2 lines && [ ${#lines} -ne 0 ]; do
    a="${lines[0]:0:-1}"; b="${lines[1]:0:-1}"; declare -p a b;
  done

  declare -- a="1"
  declare -- b="2"
  declare -- a="3"
  declare -- b="4"