15

I have this simple task and I've spent a few hours already trying to figure out how can I use a variable inside a curl call within my bash script:

message="Hello there"
curl -X POST -H 'Content-type: application/json' --data '{"text": "${message}"}'

This is outputting ${message}, literally because it's inside a single quote. If I change the quotes and put double outside and single inside, it says command not found: Hello and then command not found: there.

How can I make this work?

Bravi
  • 713
  • 2
  • 8
  • 29

2 Answers2

41

Variables are not expanded within single-quotes. Rewrite using double-quotes:

curl -X POST -H 'Content-type: application/json' --data "{\"text\": \"${message}\"}"

Just remember that double-quotes within double-quotes have to be escaped.

Another variation could be:

curl -X POST -H 'Content-type: application/json' --data '{"text": "'"${message}"'"}'

This one breaks out of the single quotes, encloses ${message} within double-quotes to prevent word splitting, and then finishes with another single-quoted string. That is:

... '{"text": "'"${message}"'"}'
    ^^^^^^^^^^^^
    single-quoted string


... '{"text": "'"${message}"'"}'
                ^^^^^^^^^^^^
                double-quoted string


... '{"text": "'"${message}"'"}'
                            ^^^^
                            single-quoted string

However, as the other answer and @Charles Duffy pointed out in a comment, this is not a robust solution, because literal " and other characters in $message may break the JSON.

Use the other solution, which passes the content of $message to jq in a safe way, and jq takes care of correct escaping.

janos
  • 120,954
  • 29
  • 226
  • 236
  • Damn it! I knew it was something very simple! :)) Thank you. I will accept the answer as soon as I'm able to. – Bravi Nov 28 '16 at 20:25
  • I like the second one more. I was actually close to that one. One of my versions looked like this `'{"text": "'${message}'"}'`. So I just needed the inner double quotes, but the error was not very helpful to get a hint on that. – Bravi Nov 28 '16 at 20:28
  • This doesn't work correctly with messages that contain literal `"`s, messages that contain literal newlines, or any other string that needs to be escaped before it's valid JSON string contents. Answers using `jq` are safer. – Charles Duffy Nov 02 '22 at 19:46
13

While the other post (and shellcheck) correctly points out that single quotes prevent variable expansion, the robust solution is to use a JSON tool like jq:

message="Hello there"
curl -X POST -H 'Content-type: application/json' \
    --data "$(jq -n --arg var "$message"  '.text = $var')"

This works correctly even when $message contains quotes and backslashes, while just injecting it in a JSON string can cause data corruption, invalid JSON or security issues.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 5
    Even better, just pipe the output of `jq` to `curl` and use `@-` as the argument to `--data`. This eliminates a layer of quoting. – chepner Nov 28 '16 at 21:01