Exit codes for bash pipelines

When building a pipeline for a shell script, you may come across an unpleasant situation: some program fails and returns a nonzero exit code, but one of the subsequent programs does not register an error and returns an exit code of zero.

Why is this a problem? Consider the case where you want to download a file while simultaneously getting its hash (see the tee man page if you're not familiar with tee):

  MD5=$(wget http://example.com/some.file -O - | tee ~/some.file.saved | md5sum)

The problem here is that wget may return an error (due to a 404, for example), but md5sum still returns 0. Thus if you check the exit code with $? -- it's zero and everything seems to have gone okay.

bash actually has a really cool feature, where the return values for items in a pipeline are put in an array called PIPESTATUS, such that ${PIPESTATUS[0]} is the exit code from the first command, ${PIPESTATUS[1]} the exit code from the second command, etc. The problem is that with an example like the above -- where output is being captured -- this doesn't work. ${PIPESTATUS[0]} is just $?, and no other elements are set (I didn't put too much thought into whether there's a way around this, though).

As it turns out, though, bash has an option called "pipefail".

Bash Manual wrote:
If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully.

This, of course, is exactly what we want. Enable it with "set -o pipefail". Problem solved.

Trackback URL for this post:

http://www.constantthought.com/trackback/5