Curl with multiline of JSON - bash

Consider the curl command below, is it possible to allow newline in JSON (without the minify) and execute directly in bash (Mac/Ubuntu)
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
-d \
'
{
"field1": "test",
"field2": {
"foo": "bar"
}
}'
When I run the command above, seems error occurred at the second {
How to fix the above command?
Updated: actually I was able to run the command without issue previously, not sure why problem happen recently.

I remembered another way to do this with a "Here Document" as described in the Bash man page and detailed here. The #- means to read the body from STDIN, while << EOF means to pipe the script content until "EOF" as STDIN to curl. This layout may be easier to read than using separate files or the "echo a variable" approach.
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: application/json; charset=utf-8' \
--data-binary #- << EOF
{
"field1": "test",
"field2": {
"foo": "bar"
}
}
EOF
NOTE: Use the --trace <outfile> curl option to record exactly what goes over the wire. For some reason, this Here Document approach strips newlines. (Update: Newlines were stripped by curl -d option. Corrected!)

Along the lines of Martin's suggestion of putting the JSON in a variable, you could also put the JSON in a separate file, and then supply the filename to -d using curl's # syntax:
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
-d #myfile.json
The disadvantage is obvious (2 or more files where you used to have one.) But on the plus side, your script could accept a filename or directory argument and you'd never need to edit it, just run it on different JSON files. Whether that's useful depends on what you are trying to accomplish.

For some reason, this Here Document approach strips newlines
#eric-bolinger the reason the Heredoc strips newlines is because you need to tell your Heredoc to preserve newlines by quoting the EOF:
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
-d #- <<'EOF'
{
"field1": "test",
"field2": {
"foo": "bar"
}
}
EOF
Notice the single-ticks surrounding EOF the first time it's defined, but not the second.

You should use outer double quotes, and the escape all inner quotes like this:
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
-d \
"
{
\"field1\": \"test\",
\"field2\": {
\"foo\": \"bar\"
}
}"

You could assign your json to a var:
json='
{
"field1": "test",
"field2": {
"foo": "bar"
}
}'
Now you can forward this to curl using stdin:
echo $json | curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
-d #-

I think this can be an answer
curl -0 -v -X POST http://www.example.com/api/users \
-H "Expect:" \
-H 'Content-Type: text/json; charset=utf-8' \
--data-raw '
{
"field1": "test",
"field2": {
"foo": "bar"
}
}'

Related

GET CURL doesn't escape some of the characters [duplicate]

This question already has answers here:
Escaping characters in bash (for JSON)
(13 answers)
Parsing JSON with Unix tools
(45 answers)
Closed 10 days ago.
I have the following GET CURL from which I get an xml.
curl -X 'GET' \
'http://local/something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth'
Now I want to use the previous xml received above within this POST CURL:
curl -X 'POST' \
'http://something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth' \
-H 'Content-Type: application/json' \
-d '{
"components": [
{
"locator": "sample",
"config": xml file from above
}
]
}'
How can I make the second CURL with POST?
See this post to see how to capture the output of the first command into a variable. Use it like this:
output=$(curl -X 'GET' \
'http://local/something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth')
# Assuming the $output variable is a JSON object, with a property
# called 'result', use 'jq' to extract the value of that property
result=$(jq -r '.result' <<< "$output")
# As noted above, escape the double quotes with backslashes
curl -X 'POST' \
'http://something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth' \
-H 'Content-Type: application/json' \
-d "{
\"components\": [
{
\"locator\": \"sample\",
\"config\": \"$result\"
}
]
}"
Note the double quotes - double quotes must be there so $output variable can be used. As a result, the double quotes in the JSON need to be escaped.

Use CURL POST using a CURL GET output in bash [duplicate]

This question already has answers here:
Escaping characters in bash (for JSON)
(13 answers)
Parsing JSON with Unix tools
(45 answers)
Closed 10 days ago.
I have the following GET CURL from which I get an xml.
curl -X 'GET' \
'http://local/something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth'
Now I want to use the previous xml received above within this POST CURL:
curl -X 'POST' \
'http://something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth' \
-H 'Content-Type: application/json' \
-d '{
"components": [
{
"locator": "sample",
"config": xml file from above
}
]
}'
How can I make the second CURL with POST?
See this post to see how to capture the output of the first command into a variable. Use it like this:
output=$(curl -X 'GET' \
'http://local/something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth')
# Assuming the $output variable is a JSON object, with a property
# called 'result', use 'jq' to extract the value of that property
result=$(jq -r '.result' <<< "$output")
# As noted above, escape the double quotes with backslashes
curl -X 'POST' \
'http://something/something2' \
-H 'accept: application/json' \
-H 'authorization: auth' \
-H 'Content-Type: application/json' \
-d "{
\"components\": [
{
\"locator\": \"sample\",
\"config\": \"$result\"
}
]
}"
Note the double quotes - double quotes must be there so $output variable can be used. As a result, the double quotes in the JSON need to be escaped.

How to make a valid curl request in shell script? [duplicate]

Goal:
I'm using a bash CURL script to connect to the Cloudflare APIv4. The goal is to update an A-record. My script:
# Get current public IP
current_ip=curl --silent ipecho.net/plain; echo
# Update A record
curl -X PUT "https://api.cloudflare.com/client/v4/zones/ZONEIDHERE/dns_records/DNSRECORDHERE" \
-H "X-Auth-Email: EMAILHERE" \
-H "X-Auth-Key: AUTHKEYHERE" \
-H "Content-Type: application/json" \
--data '{"id":"ZONEIDHERE","type":"A","name":"example.com","content":"'"${current_ip}"'","zone_name":"example.com"}'
Problem:
The current_ip variable is not printed when I call it in my script. The output will be "content" : "" and not "content" : "1.2.3.4".
I used other stackoverflow posts and I'm trying to follow their examples but I think I'm still doing something wrong, just can't figure out what. :(
Using jq for this, as Charles Duffy's answer suggests, is a very good idea. However, if you can't or do not want to install jq here is what you can do with plain POSIX shell.
#!/bin/sh
set -e
current_ip="$(curl --silent --show-error --fail ipecho.net/plain)"
echo "IP: $current_ip"
# Update A record
curl -X PUT "https://api.cloudflare.com/client/v4/zones/ZONEIDHERE/dns_records/DNSRECORDHERE" \
-H "X-Auth-Email: EMAILHERE" \
-H "X-Auth-Key: AUTHKEYHERE" \
-H "Content-Type: application/json" \
--data #- <<END;
{
"id": "ZONEIDHERE",
"type": "A",
"name": "example.com",
"content": "$current_ip",
"zone_name": "example.com"
}
END
The reliable way to edit JSON from shell scripts is to use jq:
# set shell variables with your contents
email="yourEmail"
authKey="yourAuthKey"
zoneid="yourZoneId"
dnsrecord="yourDnsRecord"
# make sure we show errors; --silent without --show-error can mask problems.
current_ip=$(curl --fail -sS ipecho.net/plain) || exit
# optional: template w/ JSON content that won't change
json_template='{"type": "A", "name": "example.com"}'
# build JSON with content that *can* change with jq
json_data=$(jq --arg zoneid "$zoneid" \
--arg current_ip "$current_ip" \
'.id=$zoneid | .content=$current_ip' \
<<<"$json_template")
# ...and submit
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$dnsrecord" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $authKey" \
-H "Content-Type: application/json" \
--data "$json_data"

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/hipchatmsg.sh "my great message here \ " red
And my code in my bash script (hipchatmsg.sh) 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
else
MESSAGE=$1
fi
// send locally via curl
/usr/bin/curl -H "Content-Type: application/json" \
-X POST \
-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" \
-X POST \
-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.

How to use a variable in a CURL request with bash?

Goal:
I'm using a bash CURL script to connect to the Cloudflare APIv4. The goal is to update an A-record. My script:
# Get current public IP
current_ip=curl --silent ipecho.net/plain; echo
# Update A record
curl -X PUT "https://api.cloudflare.com/client/v4/zones/ZONEIDHERE/dns_records/DNSRECORDHERE" \
-H "X-Auth-Email: EMAILHERE" \
-H "X-Auth-Key: AUTHKEYHERE" \
-H "Content-Type: application/json" \
--data '{"id":"ZONEIDHERE","type":"A","name":"example.com","content":"'"${current_ip}"'","zone_name":"example.com"}'
Problem:
The current_ip variable is not printed when I call it in my script. The output will be "content" : "" and not "content" : "1.2.3.4".
I used other stackoverflow posts and I'm trying to follow their examples but I think I'm still doing something wrong, just can't figure out what. :(
Using jq for this, as Charles Duffy's answer suggests, is a very good idea. However, if you can't or do not want to install jq here is what you can do with plain POSIX shell.
#!/bin/sh
set -e
current_ip="$(curl --silent --show-error --fail ipecho.net/plain)"
echo "IP: $current_ip"
# Update A record
curl -X PUT "https://api.cloudflare.com/client/v4/zones/ZONEIDHERE/dns_records/DNSRECORDHERE" \
-H "X-Auth-Email: EMAILHERE" \
-H "X-Auth-Key: AUTHKEYHERE" \
-H "Content-Type: application/json" \
--data #- <<END;
{
"id": "ZONEIDHERE",
"type": "A",
"name": "example.com",
"content": "$current_ip",
"zone_name": "example.com"
}
END
The reliable way to edit JSON from shell scripts is to use jq:
# set shell variables with your contents
email="yourEmail"
authKey="yourAuthKey"
zoneid="yourZoneId"
dnsrecord="yourDnsRecord"
# make sure we show errors; --silent without --show-error can mask problems.
current_ip=$(curl --fail -sS ipecho.net/plain) || exit
# optional: template w/ JSON content that won't change
json_template='{"type": "A", "name": "example.com"}'
# build JSON with content that *can* change with jq
json_data=$(jq --arg zoneid "$zoneid" \
--arg current_ip "$current_ip" \
'.id=$zoneid | .content=$current_ip' \
<<<"$json_template")
# ...and submit
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$dnsrecord" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $authKey" \
-H "Content-Type: application/json" \
--data "$json_data"

Resources