view raw
willdanceforfun willdanceforfun - 10 months ago 50
Bash Question

How can I post a json string with curl that has characters that need escaping?

I've got a shell script I've been using to post stuff to a hipchat channel. It works ok until I try and send a message that has characters that need escaping. I run the command like so (note the extra backslash in there to cause a problem)

/usr/local/bin/ "my great message here \ " red

And my code in my bash script ( that matters is this:

# Make sure message is passed
if [ -z ${1+x} ]; then
echo "Provide a message to create the new notification"
exit 1

// send locally via curl
/usr/bin/curl -H "Content-Type: application/json" \
-k \
-d "{\"color\": \"$COLOR\", \"message_format\": \"text\", \"message\": \"$MESSAGE\" }" \
$SERVER/v2/room/$ROOM_ID/notification?auth_token=$AUTH_TOKEN &

// $server and $room are defined earlier

exit 0

If I try and run the command above with any characters that need escaping, I will get an error like this:

"error": {
"code": 400,
"message": "The request body cannot be parsed as valid JSON: Invalid \\X escape sequence u'\\\\': line 1 column 125 (char 124)",
"type": "Bad Request"

I found something kind of similar on here where the best advice was to try sending the curl post with --data-urlencode, so I tried like this:

/usr/bin/curl -H "Content-Type: application/json" \
-k \
-d --data-urlencode "{\"color\": \"$COLOR\", \"message_format\": \"text\", \"message\": \"$MESSAGE\" }" \
$SERVER/v2/room/$ROOM_ID/notification?auth_token=$AUTH_TOKEN &

But this had no effect.

What am I missing here?


The easiest thing to do is use a program like jq to generate the JSON; it will take care of escaping what needs to be escaped.

jq -n --arg color "$COLOR" \
      --arg message "$MESSAGE" \
   '{color: $color, message_format: "text", message: $message}' |
 /usr/bin/curl -H "Content-Type: application/json" \
   -X POST \
   -k \
   -d@- \
   $SERVER/v2/room/$ROOM_ID/notification?auth_token=$AUTH_TOKEN &

The argument @- to -d tells curl to read from standard input, which is supplied from jq via the pipe. The --arg options to jq make available JSON-encoded strings to the filter, which is simply a JSON object expression.