This post summarizes how to get the exit status from commands in a pipeline.
Introduction
When you try to get the exit status of a pipeline using $?, by default, it only returns the exit status of the last command.
#!/bin/bash exit 1 | exit 2 | exit 0 echo $? # Output: 0
Since the exit statuses of the earlier commands in the pipeline are masked, it can be problematic if you want to detect failures within the pipeline.
This post explains how to get the exit statuses of all commands in a pipeline.
Note: This article was translated from my original post.
How to Get Exit Statuses from a Pipeline
Using PIPESTATUS
First, let's look at the PIPESTATUS variable.
This array holds the exit statuses of all commands in the most recently executed pipeline.
You can view the statuses of all the commands in the pipeline like this:
#!/bin/bash exit 1 | exit 2 | exit 0 echo "${PIPESTATUS[@]}" # Output: 1 2 0
You can also access individual statuses by specifying the index:
#!/bin/bash exit 1 | exit 2 | exit 0 echo "${PIPESTATUS[0]}" "${PIPESTATUS[1]}" # Output: 1 2
Using set -o pipefail
Next, there's the set -o pipefail option.
This setting causes the pipeline to return the exit status of the last command that exited with a non-zero status. If all commands succeed, it returns 0.
This makes it possible to detect failures inside the pipeline without masking them.
Quoting from the set command man page:
the return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status
In practice, it works like this:
#!/bin/bash set -o pipefail exit 1 | exit 2 | exit 0 echo $? # Output: 2
Conclusion
This post explained how to get exit statuses from pipelines.
Pipelines are a powerful feature, so it's important to get used to handling them properly, including detailed error checking.
[Related Articles]