0

How do I nest a variable that is a string inside of another variable that is a saved output of a command?

I want to accomplish what this code does:

xmrprice=$(curl -s 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true' |jq -r '.monero'|jq -r '.usd')
echo "The price of Monero is $xmrprice"
btcprice=$(curl -s 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true' |jq -r '.bitcoin'|jq -r '.usd')
echo "The price of BTC is $btcprice"
ltcprice=$(curl -s 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true' |jq -r '.litecoin'|jq -r '.usd')
echo "The price of Litecoin is $ltcprice"

but without calling curl three times. Removing the -s flag would demonstrate that curl is indeed called three times. I want to assign the string from the output of

curl -s https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true

to a variable and then extract from it the values of xmrprice, brcprice, and ltcprice. Much like this Python code does it:

#!/usr/bin/python3

#pip3 install requests

import requests
import json

result = requests.get("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true", headers = {"accept":"application/json"})

print("Monero's and Bitcoin's prices as of now:")
print(str(result.json()["monero"]["usd"]))
print(str(result.json()["bitcoin"]["usd"]))

I have come across this question on nesting variables in Bash but it relates more to assigning a nested outputs of multiple commands to a variable.

I've tried all this:

response=$(curl -s https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true)
#xmrprice=(${response}|jq -r '.monero'|jq -r '.usd') #syntax error near unexpected token `|'
#xmrprice=("${response}"|jq -r '.monero'|jq -r '.usd') #syntax error near unexpected token `|'
#xmrprice=$("${response}"|jq -r '.monero'|jq -r '.usd') #{"error":"Missing parameter vs_currencies"}: command not found
#xmrprice=$("{response}"|jq -r '.monero'|jq -r '.usd') #: {response}: command not found

echo "The price of Monero is ${xmrprice}". 

None of it works. The comments describe the errors that I got from Bash.

2 Answers 2

3

I would probably do this:

#!/bin/sh

unset -v xmrprice btcprice ltcprice

url='https://api.coingecko.com/endpoint/path'

eval "$(
    curl -s "$url" |
    jq -r '
        @sh "xmrprice=\(.monero.usd|tonumber)",
        @sh "btcprice=\(.bitcoin.usd|tonumber)",
        @sh "ltcprice=\(.litecoin.usd|tonumber)"'
)"

printf '%s is %s\n' \
    'xmrprice' "$xmrprice" \
    'btcprice' "$btcprice" \
    'ltcprice' "$ltcprice"

This calls curl once and passes the resulting document through a single jq invocation without having to store it in a file or variable. The jq expression creates three strings. Each string is a variable assignment. The @sh operator in jq makes sure that the string is properly quoted for the shell.

The eval evaluates the variable assignments, creating the three shell variables.

Note that none of the quotes used in the code, whether single or double, are accidental or unneeded (apart from the single quotes around the plain strings in the arguments to printf at the end, they are just for looking good).


If you're not actually interested in creating shell variables, then there is no need to do so as jq may be used to do the formatted output:

#!/bin/sh

url='https://api.coingecko.com/endpoint/path'

curl -s "$url" |
jq -r '
    "xmrprice is at \(.monero.usd|tonumber) USD",
    "btcprice is at \(.bitcoin.usd|tonumber) USD",
    "ltcprice is at \(.litecoin.usd|tonumber) USD"'
0
0

You are close to what you're looking for. You're only missing an echo.

#!/usr/bin/env bash
response=$(curl -s 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin%2Cbitcoin-cash%2Clitecoin%2Cmonero&vs_currencies=usd&include_market_cap=true')
xmrprice=$(echo "${response}"|jq -r '.monero'|jq -r '.usd')
echo "${xmrprice}"

Edit: As pointed out by Kusalananda, you are also missing the quotes around the URL in your curl command.

5
  • 1
    You need to quote the URL or you break that line apart into a curl call and two variable assignments. Why do you call jq twice? jq -r '.monero.usd' would work too... Also note that echo may modify the string that it is outputting by interpreting some backslash character sequences.
    – Kusalananda
    Aug 3, 2021 at 15:32
  • I only wanted to help around the bash issues, not the jq ones.
    – baku
    Aug 3, 2021 at 15:36
  • 1
    You shouldn't use echo for arbitrary data, especially those that are likely to contain backslash characters such as json data. See Why is printf better than echo? Aug 3, 2021 at 15:43
  • @Stéphane printf "%s\n" "$xmrprice" instead of using echo would do?
    – John Smith
    Aug 3, 2021 at 16:17
  • 1
    @JerzyBrzóska, yes, though it's probably even more important to do it for $response which is the one likely to contain backslashes. printf '%s\n' "$response" | .... In the case of jq, the trailing newline is not necessary, so printf %s "$response" | jq... would be enough. Aug 3, 2021 at 16:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .