15 June 2006

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.