Pragmatism in the real world

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.

Thoughts? Leave a reply

Your email address will not be published. Required fields are marked *