Getting status code and body from curl in a bash script
When writing a shell script recently, I realised that it would be really handy to get the the status code from a curl command in addition to the body.
Usually, I call curl like this:
body=$(curl -s "http://localhost:8888/") echo "Curl exit code: $?" echo "Body: $body"
This works well, but no status code is available as curl’s exit code is related to its own error system, not the HTTP call.
Searching Stack Overflow, I came across this question and adapted one of the answers to create a function encapsulates the code required, so that it doesn’t end up in the rest of my script:
curlwithcode() {
# Run curl, putting the HTTP status code in $status_code and the body into $body
# Based on https://stackoverflow.com/a/52832805/23060
local code=0
local tmpfile; tmpfile=$(mktemp /tmp/curlwithcode.XXXXXX)
status_code=$(curl -s -w "%{http_code}" -o >(cat >"$tmpfile") "$@") || code="$?"
body="$(cat "$tmpfile")"
rm "tmpfile"
return $code
}
This relies on a couple of features of curl.
Firstly, we need to display the status code. This is done using -w "%{http_code}" which will write out the status code to stdout.
It looks like this:
curl -s "http://localhost:8888/" -w "%{http_code}" { "links": { "games": "/games" } }200
The status code of 200 is output immediately after the body. Therefore we use the -o parameter to write the body to a temporary file. This leaves just the status code on stdout for reading:
$ curl -s "http://localhost:8888/" -w "%{http_code}" -o /tmp/body 200
Rather than using a hardcoded filename, we use mktemp so that it’s randomised and then we can cat the file into a variable before deleting it.
One thing about bash functions is that any variables defined without the local keyword are available after the function call, so by setting status_code and body, they are available to the script, leaving the return value to be curl’s exit code.
We call it like this within the bash script:
curlwithcode "http://localhost:8888/" echo "Curl exit code: $?" echo "Status code: $status_code" echo "Body: $body"
This neatly solves my requirements.
I looked at curl(1) and discovered I prefer to have http_code on stdout since I almost always use -o, so I added it to my ~/.curlrc file instead:
———-
-sSL
-w "%{stdout}%{http_code}\n"
———-