PIPESTATUS
tl;dr: Bash’s $PIPESTATUS allows you to inspect the exit status of every command
in a pipeline.
According to Bash’s manpage, $PIPESTATUS is…
An array variable… containing a list of exit status values from the commands in the most-recently-executed foreground pipeline, which may consist of only a simple command… Bash sets PIPESTATUS after executing multi-element pipelines, timed and negated pipelines, simple commands, subshells created with the ( operator, the [[ and (( compound commands, and after error conditions that result in the shell aborting command execution.
So it’s almost like $?[1] on steroids. Especially useful when using the yes
command[2] with interactive commands.
For example:
set -o pipefail
yes | some_interactive_command
If some_interactive_command closes its stdin stream while yes is trying to write to
it (which is not uncommon), yes will exit with an exit status of 141. Since we ran
set -o pipefail, that will crash the script (even though some_interactive_command
may have exited with status 0 “success”).
Let’s modify the script a bit:
set -o pipefail
yes | some_interactive_command || {
echo "Exited with status $?"
}
This allows the script to keep running, but… sadly it reports the exit status of
yes, rather than some_interactive_command.
Exited with status 141
That’s almost never what you want. Nobody cares about yes. We only care about the
interactive command.
set -o pipefail
yes | some_interactive_command || {
echo "Programs in the pipeline exited with these exit statuses: ${PIPESTATUS[*]}"
}
Here we’ll see this output:
Programs in the pipeline exited with these exit statuses: 141 0
yesexited with status 141some_interactive_commandexited with status 0
So taking it just one step further…
set -o pipefail
yes | some_interactive_command || {
echo "Exited with status ${PIPESTATUS[1]}"
}
Exited with status 0
That’s more like it. Now we’re looking at the exit status of the command we actually care about. Putting it in a real-world script could look something like this:
set -o pipefail
panic() {
echo "$*" >&2
exit 1
}
yes | some_interactive_command || {
result=${PIPESTATUS[1]}
test ${result} -eq 0 || panic "some_interactive_command exited with status ${result}"
}
Footnotes
$?expands to the exit status of the most recently used command ↩︎yes(manpage) is super handy for answering “yes / no” prompts in interactive commands. By default it just writesyto stdout over and over, which is usually enough to get through all those prompts, and effectively makes an interactive command suitable for scripting. ↩︎
Other Posts
- Previous: tmpshell — 2025-10-16