Bash – Catching errors within a piped command chain

When chaining commands in Bash using the pipe ‘|’ separator, the commands execute in sequence, with the output of the first command being used as the input to the second command. But what happens if an error occurs when executing a command within a piped list of commands?

When chaining commands in Bash using the pipe ‘|’ separator, the commands execute in sequence, with the output of the first command being used as the input to the second command.

When running a single Bash command, it is possible to detect if an error has occurred by examining the ‘$?’ built-in Bash variable directly after the command has completed. A zero ‘0’ return value means success, a non-zero return value means that an error has occured.

For example, trying to write output to a new file in the root of the file system.

bash ~$ echo "Create a file with one line in it" > /new-file.txt
-bash: /new-file.txt: Permission denied
bash ~$ echo $?
1

Whereas writing a file to your home path will succeed as expected.

bash ~$ echo "Create a file with one line in it" > ~/new-file.txt
bash ~$ echo $?
0

But what happens if an error occurs when executing a command within a piped list of commands? Well in this case, the default is for the last command to set the return value for the entire command chain.

bash ~$ cat /new-file.txt | wc
cat: /new-file.txt: No such file or directory
       0       0       0
bash ~$ echo $?
0

This can be overridden by setting the pipefail Bash option.

bash ~$ set -o pipefail
bash ~$ cat /new-file.txt | wc
cat: /new-file.txt: No such file or directory
       0       0       0
bash ~$ echo $?
1

This can be extremely useful if you regularly chain commands together, as the return value can be used to display a message to the user indicating an issue has occurred within the command chain.