Retrieve the exit code of all the commands in a pipeline with “PIPESTATUS”

When developing shell scripts, it is common usage to check the exit code of a command in order to check if its execution was successful or not. It is pretty easy when you execute only one command, because the exit code can be find using the special parameter “$?”. But if you use pipelines (“command1 | command2 | …”), it is not that simple because “$?” will only give you the exit code of the last command of your pipeline.

So, today, I’m going to show you how to get the exit code for each command in a pipeline. Unfortunately, this only works in bash.

Simple test

Let’s start with a simple test.

Here, I’m trying to print the contents of a non-existent file and check the exit code:

[root@linuxlab01 tmp]# cat titi
cat: titi: No such file or directory

[root@linuxlab01 tmp]# echo $?
1

As expected, the exit code is 1 because the file does not exist.

Now, I’m going to pipe the output of the “cat” command on a non-existent file to the “awk” command:

[root@linuxlab01 tmp]# cat titi|awk '{print $1}'
cat: titi: No such file or directory

[root@linuxlab01 tmp]# echo $?
0

The exit code is 0 because “$?” gave me the result of the “awk” command that ran successfully despite the fact that the “cat” command failed. Fortunately, we can use the bash internal variable “PIPESTATUS” to handle this situation.

Introducing the special variable “PIPESTATUS”

The special  variable “PIPESTATUS” is an array containing the exit code of each commands executed in a pipeline. If no pipe is used, it will hold the exit code of the last executed command.

In my previous example using “cat” and “awk”, the PIPESTATUS variable contains:

PIPESTATUS[0] : 1 <----- "cat" command
PIPESTATUS[1] : 0 <----- "awk" command

The exit code of the first command of your pipe will be stored at index 0 in PIPESTATUS, command 2 at index 1 …

Very useful isn’t it? 🙂

You can use the examples of my previous post “Work with bash/ksh associative arrays” to extract the exit codes from the array but I’ll give you a few simple examples to get started.

Advices about PIPESTATUS

It is recommended to store the contents of PIPESTATUS in a temporary variable before using it. The reason is very simple: since PIPESTATUS contains the exit code of the last command(s), its value will change if you print its content several times in a row.

Let’s illustrate my words:

[root@linuxlab01 tmp]# cat titi|awk '{print $1}'
cat: titi: No such file or directory

[root@linuxlab01 tmp]# echo ${PIPESTATUS[@]}
1 0

“echo ${PIPESTATUS[@]}” returns the exit code of the “cat” and “awk” commands.
If I execute the same “echo” command again:

[root@linuxlab01 tmp]# echo ${PIPESTATUS[@]}
0

Now, the PIPESTATUS variable contains the exit code of the first echo I made in the first code block! So be very careful and always use a temporary variable to store PIPESTATUS content. Especially if you intend to use its content later in your script!

Examples

Test the exit code of one specific stage of your pipeline

cat titi|awk '{print $1}'
TMPSTATUS=("${PIPESTATUS[@]}")
if [[ ${TMPSTATUS[0]} -eq 0 ]];then
	echo "cat OK"
else
	echo "cat KO"
fi

Test the exit code of all stages of your pipeline

cat titi|awk '{print $1}'
TMPSTATUS=("${PIPESTATUS[@]}")
if [[ ${TMPSTATUS[@]} == "0 0" ]];then
	echo "Everything OK"
else
	echo "Something wrong"
fi

Including more stages in the pipeline:

cat titi|awk '{print $1}'|head|grep -c "anything"
TMPSTATUS=("${PIPESTATUS[@]}")
if [[ ${TMPSTATUS[@]} == "0 0 0 0" ]];then
	echo "Everything OK"
else
	echo "Something wrong"
fi

If you want to identify exactly which command failed:

cat titi|awk '{print $1}'|head|grep -c "anything"
TMPSTATUS=("${PIPESTATUS[@]}")
if [[ ${TMPSTATUS[0]} -ne 0 ]];then
	echo "cat KO"
elif [[ ${TMPSTATUS[1]} -ne 0 ]];then
	echo "awk KO"
elif [[ ${TMPSTATUS[2]} -ne 0 ]];then
	echo "head KO"
elif [[ ${TMPSTATUS[3]} -ne 0 ]];then
	echo "grep KO"
else
	echo "Everything OK"
fi

I hope these tips will be useful for your scripting work. Stay tuned for more DBA stuff!

2 thoughts on “Retrieve the exit code of all the commands in a pipeline with “PIPESTATUS”

  1. Hello guy,

    possible to test the sum or the array. If >0, then there’s a problem.
    Don’t matter how many commands have been launched
    ( IFS=+; echo “${TMPSTATUS[@]}” ) | bc

Leave a Reply

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