Shell scripting: getting exit status from a piped command

Some programs don't behave nicely when used in a pipe on unix-like systems. For example, I was using pg_dump piped through gzip like so:

Broken script: error condition never occurs

export PGPASSWORD=secret
pg_dump -h host -U user db | gzip - > backup.sql.gz

if [ $? -ne 0 ]; then
    ### Never happens: gzip never fails! ###
    echo Backup failed.
    exit 1
fi

This uses the normal way of checking the exit status of the previous command ($?), but it doesn't work. If the pg_dump fails for any reason, gzip doesn't return any error response. $? is set to 0, indicating success.

Fortunately, there's a better way. In bash, the PIPESTATUS environment variable is an array with the return codes of all the commands executed in the last pipe. Checking for the overall return status and the status of pg_dump is now done like this:

Correct script: check the result of pg_dump separately

export PGPASSWORD=secret
pg_dump -h host -U user db | gzip - > backup.sql.gz

if [ $? -ne 0 -o ${PIPESTATUS[0]} -ne 0 ]; then
    echo Backup failed.
    exit 1
fi

Now I can be sure my automated database backups aren't going to fail silently.

Portrait of Matt Ryall

About Matt

I’m a technology nerd, husband and father of four, living in beautiful Sydney, Australia.

My passion is building software products that make the world a better place. For the last 15 years, I’ve led product teams at Atlassian to create collaboration tools.

I'm also a startup advisor and investor, with an interest in advancing the Australian space industry. You can read more about my work on my LinkedIn profile.

To contact me, please send an email or reply on Twitter.