The Shell Scripting Tutorial


Checking the exit status of ANY command in a pipeline

Normally you only get the exit status of the last command in a pipeline - this way, you can get the status of any command in a pipline of any length...

30 Oct 2015

It's a pretty common thing in a shell script to want to check the exit status of the previous command. You can do this with the $? variable, as is widely known:

#!/bin/bash
grep some.machine.example.com /etc/hosts
if [ "$?" -ne "0" ]; then
  # The grep command failed to find "some.machine.example.com" in /etc/hosts file
  echo "I don't know the IP address of some.machine.example.com"
  exit 2
fi

What gets difficult is when you execute a pipeline: (see pipelines for more information on the Unix Pipeline)

#!/bin/bash
grep some.machine.example.com /etc/hosts 2>&1 | tee /tmp/hosts-results.txt
if [ "$?" -ne "0" ]; then
  # Ah - what we get here is the status of the "tee" command, 
  # not the status of the "grep" command :-(

What you get is the result of the tee command, which writes the results to the display as well as to the /tmp/hosts-results.txt file.

To find out what grep returned, $? is of no use.

Instead, use the ${PIPESTATUS[]} array variable. ${PIPESTATUS[0]} tells us what grep returned, while ${PIPESTATUS[1]} tells us what tee returned.

So, to see what grep found, we can write our script like this:

#!/bin/bash
grep some.machine.example.com /etc/hosts 2>&1 | tee /tmp/hosts-results.txt
if [ "${PIPESTATUS[0]}" -ne "0" ]; then
  # The grep command failed to find "some.machine.example.com" in /etc/hosts file
  echo "I don't know the IP address of some.machine.example.com"
  exit 2
fi

Here's The Rub

The downside is, that any command you use to access ${PIPESTATUS[]}, will automatically replace the current state of the array with the return code of the command you have just run:

Pipeline (command)PIPESTATUS shows status of:
grep ... | tee ...grep,tee
echo "Grep returned ${PIPESTATUS[0]}"echo "Grep ...
echo "Maybe PIPESTATUS isn't so useful after all"echo "Maybe ...

So as soon as we use echo to tell us about the return code of grep, the ${PIPESTATUS[]} array now tells us about the return code of the echo statement itself, which is pretty likely to be zero, as not much can cause echo to fail!

The Fix

Because ${PIPESTATUS[]} is a special variable, it changes all the time. However, we can copy this array into another array, which is just a regular array, which will not be changed at all just by running some more commands. Copying an array requires a slightly different syntax to simply copying contents of a variable into another:

RC=( "${PIPESTATUS[@]}" )

Where RC stands for Return Code. We can then investigate the status of RC at our leisure. For testing purposes, the program true always returns zero, and false always returns 1:

#!/bin/bash
echo "tftf"
true | false | true | false
RC=( "${PIPESTATUS[@]}" )
echo "RC[0] = ${RC[0]}"		# true = 0
echo "RC[1] = ${RC[1]}"		# false = 1
echo "RC[2] = ${RC[2]}"		# true = 0
echo "RC[3] = ${RC[3]}"		# false = 1

echo "ftft"
false | true | false | true
RC=( "${PIPESTATUS[@]}" )
echo "RC[0] = ${RC[0]}"		# false = 1
echo "RC[1] = ${RC[1]}"		# true = 0
echo "RC[2] = ${RC[2]}"		# false = 1
echo "RC[3] = ${RC[3]}"		# true = 0

echo "fftt"
false | false | true | true
RC=( "${PIPESTATUS[@]}" )
echo "RC[0] = ${RC[0]}"		# false = 1
echo "RC[1] = ${RC[1]}"		# false = 1
echo "RC[2] = ${RC[2]}"		# true = 0
echo "RC[3] = ${RC[3]}"		# true = 0

Download the tftf.sh script

True/False/True/False

You can use this for more nuanced checking of return codes, such as this combination of curl and grep.

#!/bin/bash
WEB_SERVER=steve-parker.org
curl -# -f -u ${USERNAME}:${PASSWORD} http://${WEB_SERVER}/ | grep "SomeMessage"
RC=( "${PIPESTATUS[@]}" )
if [ "${RC[0]}" -eq "22" ]; then
  # curl returned 22, indicating some error above 400,
  # such as: 404 Not Found, 401 Unauthorized, etc.
  echo "Invalid credentials"
  exit 1
fi

# PIPESTATUS has gone, but we can still inspect RC
if [ "${RC[1]}" -eq "0" ]; then
  echo "Grep succeeded"
  echo "Web server reported SomeMessage"
else
  echo "Web server didn't report SomeMessage"
fi

Download the curlgrep.sh script

Curl | Grep


My Paperbacks and eBooks

My Shell Scripting books, available in Paperback and eBook formats. This tutorial is more of a general introduction to Shell Scripting, the longer Shell Scripting: Expert Recipes for Linux, Bash and more book covers every aspect of Bash in detail.

Shell Scripting Tutorial

Shell Scripting Tutorial
is this tutorial, in 88-page Paperback and eBook formats. Convenient to read on the go, and in paperback format good to keep by your desk as an ever-present companion.

Also available in PDF form from Gumroad:Get this tutorial as a PDF
Shell Scripting: Expert Recipes for Linux, Bash and more

Shell Scripting: Expert Recipes for Linux, Bash and more
is my 564-page book on Shell Scripting. The first half covers all of the features of the shell in every detail; the second half has real-world shell scripts, organised by topic, along with detailed discussion of each script.